├── ERC20.sol ├── Simple Token Swap └── index.ts /ERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | // File: @openzeppelin/contracts/token/ERC20/IERC20.sol 4 | 5 | 6 | // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) 7 | 8 | pragma solidity ^0.8.20; 9 | 10 | /** 11 | * @dev Interface of the ERC20 standard as defined in the EIP. 12 | */ 13 | interface IERC20 { 14 | /** 15 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 16 | * another (`to`). 17 | * 18 | * Note that `value` may be zero. 19 | */ 20 | event Transfer(address indexed from, address indexed to, uint256 value); 21 | 22 | /** 23 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 24 | * a call to {approve}. `value` is the new allowance. 25 | */ 26 | event Approval(address indexed owner, address indexed spender, uint256 value); 27 | 28 | /** 29 | * @dev Returns the value of tokens in existence. 30 | */ 31 | function totalSupply() external view returns (uint256); 32 | 33 | /** 34 | * @dev Returns the value of tokens owned by `account`. 35 | */ 36 | function balanceOf(address account) external view returns (uint256); 37 | 38 | /** 39 | * @dev Moves a `value` amount of tokens from the caller's account to `to`. 40 | * 41 | * Returns a boolean value indicating whether the operation succeeded. 42 | * 43 | * Emits a {Transfer} event. 44 | */ 45 | function transfer(address to, uint256 value) external returns (bool); 46 | 47 | /** 48 | * @dev Returns the remaining number of tokens that `spender` will be 49 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 50 | * zero by default. 51 | * 52 | * This value changes when {approve} or {transferFrom} are called. 53 | */ 54 | function allowance(address owner, address spender) external view returns (uint256); 55 | 56 | /** 57 | * @dev Sets a `value` amount of tokens as the allowance of `spender` over the 58 | * caller's tokens. 59 | * 60 | * Returns a boolean value indicating whether the operation succeeded. 61 | * 62 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 63 | * that someone may use both the old and the new allowance by unfortunate 64 | * transaction ordering. One possible solution to mitigate this race 65 | * condition is to first reduce the spender's allowance to 0 and set the 66 | * desired value afterwards: 67 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 68 | * 69 | * Emits an {Approval} event. 70 | */ 71 | function approve(address spender, uint256 value) external returns (bool); 72 | 73 | /** 74 | * @dev Moves a `value` amount of tokens from `from` to `to` using the 75 | * allowance mechanism. `value` is then deducted from the caller's 76 | * allowance. 77 | * 78 | * Returns a boolean value indicating whether the operation succeeded. 79 | * 80 | * Emits a {Transfer} event. 81 | */ 82 | function transferFrom(address from, address to, uint256 value) external returns (bool); 83 | } 84 | 85 | // File: @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol 86 | 87 | 88 | // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol) 89 | 90 | pragma solidity ^0.8.20; 91 | 92 | 93 | /** 94 | * @dev Interface for the optional metadata functions from the ERC20 standard. 95 | */ 96 | interface IERC20Metadata is IERC20 { 97 | /** 98 | * @dev Returns the name of the token. 99 | */ 100 | function name() external view returns (string memory); 101 | 102 | /** 103 | * @dev Returns the symbol of the token. 104 | */ 105 | function symbol() external view returns (string memory); 106 | 107 | /** 108 | * @dev Returns the decimals places of the token. 109 | */ 110 | function decimals() external view returns (uint8); 111 | } 112 | 113 | // File: @openzeppelin/contracts/utils/Context.sol 114 | 115 | 116 | // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) 117 | 118 | pragma solidity ^0.8.20; 119 | 120 | /** 121 | * @dev Provides information about the current execution context, including the 122 | * sender of the transaction and its data. While these are generally available 123 | * via msg.sender and msg.data, they should not be accessed in such a direct 124 | * manner, since when dealing with meta-transactions the account sending and 125 | * paying for execution may not be the actual sender (as far as an application 126 | * is concerned). 127 | * 128 | * This contract is only required for intermediate, library-like contracts. 129 | */ 130 | abstract contract Context { 131 | function _msgSender() internal view virtual returns (address) { 132 | return msg.sender; 133 | } 134 | 135 | function _msgData() internal view virtual returns (bytes calldata) { 136 | return msg.data; 137 | } 138 | 139 | function _contextSuffixLength() internal view virtual returns (uint256) { 140 | return 0; 141 | } 142 | } 143 | 144 | // File: @openzeppelin/contracts/interfaces/draft-IERC6093.sol 145 | 146 | 147 | // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol) 148 | pragma solidity ^0.8.20; 149 | 150 | /** 151 | * @dev Standard ERC20 Errors 152 | * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens. 153 | */ 154 | interface IERC20Errors { 155 | /** 156 | * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. 157 | * @param sender Address whose tokens are being transferred. 158 | * @param balance Current balance for the interacting account. 159 | * @param needed Minimum amount required to perform a transfer. 160 | */ 161 | error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed); 162 | 163 | /** 164 | * @dev Indicates a failure with the token `sender`. Used in transfers. 165 | * @param sender Address whose tokens are being transferred. 166 | */ 167 | error ERC20InvalidSender(address sender); 168 | 169 | /** 170 | * @dev Indicates a failure with the token `receiver`. Used in transfers. 171 | * @param receiver Address to which tokens are being transferred. 172 | */ 173 | error ERC20InvalidReceiver(address receiver); 174 | 175 | /** 176 | * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers. 177 | * @param spender Address that may be allowed to operate on tokens without being their owner. 178 | * @param allowance Amount of tokens a `spender` is allowed to operate with. 179 | * @param needed Minimum amount required to perform a transfer. 180 | */ 181 | error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed); 182 | 183 | /** 184 | * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. 185 | * @param approver Address initiating an approval operation. 186 | */ 187 | error ERC20InvalidApprover(address approver); 188 | 189 | /** 190 | * @dev Indicates a failure with the `spender` to be approved. Used in approvals. 191 | * @param spender Address that may be allowed to operate on tokens without being their owner. 192 | */ 193 | error ERC20InvalidSpender(address spender); 194 | } 195 | 196 | /** 197 | * @dev Standard ERC721 Errors 198 | * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens. 199 | */ 200 | interface IERC721Errors { 201 | /** 202 | * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20. 203 | * Used in balance queries. 204 | * @param owner Address of the current owner of a token. 205 | */ 206 | error ERC721InvalidOwner(address owner); 207 | 208 | /** 209 | * @dev Indicates a `tokenId` whose `owner` is the zero address. 210 | * @param tokenId Identifier number of a token. 211 | */ 212 | error ERC721NonexistentToken(uint256 tokenId); 213 | 214 | /** 215 | * @dev Indicates an error related to the ownership over a particular token. Used in transfers. 216 | * @param sender Address whose tokens are being transferred. 217 | * @param tokenId Identifier number of a token. 218 | * @param owner Address of the current owner of a token. 219 | */ 220 | error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner); 221 | 222 | /** 223 | * @dev Indicates a failure with the token `sender`. Used in transfers. 224 | * @param sender Address whose tokens are being transferred. 225 | */ 226 | error ERC721InvalidSender(address sender); 227 | 228 | /** 229 | * @dev Indicates a failure with the token `receiver`. Used in transfers. 230 | * @param receiver Address to which tokens are being transferred. 231 | */ 232 | error ERC721InvalidReceiver(address receiver); 233 | 234 | /** 235 | * @dev Indicates a failure with the `operator`’s approval. Used in transfers. 236 | * @param operator Address that may be allowed to operate on tokens without being their owner. 237 | * @param tokenId Identifier number of a token. 238 | */ 239 | error ERC721InsufficientApproval(address operator, uint256 tokenId); 240 | 241 | /** 242 | * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. 243 | * @param approver Address initiating an approval operation. 244 | */ 245 | error ERC721InvalidApprover(address approver); 246 | 247 | /** 248 | * @dev Indicates a failure with the `operator` to be approved. Used in approvals. 249 | * @param operator Address that may be allowed to operate on tokens without being their owner. 250 | */ 251 | error ERC721InvalidOperator(address operator); 252 | } 253 | 254 | /** 255 | * @dev Standard ERC1155 Errors 256 | * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens. 257 | */ 258 | interface IERC1155Errors { 259 | /** 260 | * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. 261 | * @param sender Address whose tokens are being transferred. 262 | * @param balance Current balance for the interacting account. 263 | * @param needed Minimum amount required to perform a transfer. 264 | * @param tokenId Identifier number of a token. 265 | */ 266 | error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId); 267 | 268 | /** 269 | * @dev Indicates a failure with the token `sender`. Used in transfers. 270 | * @param sender Address whose tokens are being transferred. 271 | */ 272 | error ERC1155InvalidSender(address sender); 273 | 274 | /** 275 | * @dev Indicates a failure with the token `receiver`. Used in transfers. 276 | * @param receiver Address to which tokens are being transferred. 277 | */ 278 | error ERC1155InvalidReceiver(address receiver); 279 | 280 | /** 281 | * @dev Indicates a failure with the `operator`’s approval. Used in transfers. 282 | * @param operator Address that may be allowed to operate on tokens without being their owner. 283 | * @param owner Address of the current owner of a token. 284 | */ 285 | error ERC1155MissingApprovalForAll(address operator, address owner); 286 | 287 | /** 288 | * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. 289 | * @param approver Address initiating an approval operation. 290 | */ 291 | error ERC1155InvalidApprover(address approver); 292 | 293 | /** 294 | * @dev Indicates a failure with the `operator` to be approved. Used in approvals. 295 | * @param operator Address that may be allowed to operate on tokens without being their owner. 296 | */ 297 | error ERC1155InvalidOperator(address operator); 298 | 299 | /** 300 | * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation. 301 | * Used in batch transfers. 302 | * @param idsLength Length of the array of token identifiers 303 | * @param valuesLength Length of the array of token amounts 304 | */ 305 | error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength); 306 | } 307 | 308 | // File: @openzeppelin/contracts/token/ERC20/ERC20.sol 309 | 310 | 311 | // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol) 312 | 313 | pragma solidity ^0.8.20; 314 | 315 | 316 | 317 | 318 | 319 | /** 320 | * @dev Implementation of the {IERC20} interface. 321 | * 322 | * This implementation is agnostic to the way tokens are created. This means 323 | * that a supply mechanism has to be added in a derived contract using {_mint}. 324 | * 325 | * TIP: For a detailed writeup see our guide 326 | * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How 327 | * to implement supply mechanisms]. 328 | * 329 | * The default value of {decimals} is 18. To change this, you should override 330 | * this function so it returns a different value. 331 | * 332 | * We have followed general OpenZeppelin Contracts guidelines: functions revert 333 | * instead returning `false` on failure. This behavior is nonetheless 334 | * conventional and does not conflict with the expectations of ERC20 335 | * applications. 336 | * 337 | * Additionally, an {Approval} event is emitted on calls to {transferFrom}. 338 | * This allows applications to reconstruct the allowance for all accounts just 339 | * by listening to said events. Other implementations of the EIP may not emit 340 | * these events, as it isn't required by the specification. 341 | */ 342 | abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { 343 | mapping(address account => uint256) private _balances; 344 | 345 | mapping(address account => mapping(address spender => uint256)) private _allowances; 346 | 347 | uint256 private _totalSupply; 348 | 349 | string private _name; 350 | string private _symbol; 351 | 352 | /** 353 | * @dev Sets the values for {name} and {symbol}. 354 | * 355 | * All two of these values are immutable: they can only be set once during 356 | * construction. 357 | */ 358 | constructor(string memory name_, string memory symbol_) { 359 | _name = name_; 360 | _symbol = symbol_; 361 | } 362 | 363 | /** 364 | * @dev Returns the name of the token. 365 | */ 366 | function name() public view virtual returns (string memory) { 367 | return _name; 368 | } 369 | 370 | /** 371 | * @dev Returns the symbol of the token, usually a shorter version of the 372 | * name. 373 | */ 374 | function symbol() public view virtual returns (string memory) { 375 | return _symbol; 376 | } 377 | 378 | /** 379 | * @dev Returns the number of decimals used to get its user representation. 380 | * For example, if `decimals` equals `2`, a balance of `505` tokens should 381 | * be displayed to a user as `5.05` (`505 / 10 ** 2`). 382 | * 383 | * Tokens usually opt for a value of 18, imitating the relationship between 384 | * Ether and Wei. This is the default value returned by this function, unless 385 | * it's overridden. 386 | * 387 | * NOTE: This information is only used for _display_ purposes: it in 388 | * no way affects any of the arithmetic of the contract, including 389 | * {IERC20-balanceOf} and {IERC20-transfer}. 390 | */ 391 | function decimals() public view virtual returns (uint8) { 392 | return 18; 393 | } 394 | 395 | /** 396 | * @dev See {IERC20-totalSupply}. 397 | */ 398 | function totalSupply() public view virtual returns (uint256) { 399 | return _totalSupply; 400 | } 401 | 402 | /** 403 | * @dev See {IERC20-balanceOf}. 404 | */ 405 | function balanceOf(address account) public view virtual returns (uint256) { 406 | return _balances[account]; 407 | } 408 | 409 | /** 410 | * @dev See {IERC20-transfer}. 411 | * 412 | * Requirements: 413 | * 414 | * - `to` cannot be the zero address. 415 | * - the caller must have a balance of at least `value`. 416 | */ 417 | function transfer(address to, uint256 value) public virtual returns (bool) { 418 | address owner = _msgSender(); 419 | _transfer(owner, to, value); 420 | return true; 421 | } 422 | 423 | /** 424 | * @dev See {IERC20-allowance}. 425 | */ 426 | function allowance(address owner, address spender) public view virtual returns (uint256) { 427 | return _allowances[owner][spender]; 428 | } 429 | 430 | /** 431 | * @dev See {IERC20-approve}. 432 | * 433 | * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on 434 | * `transferFrom`. This is semantically equivalent to an infinite approval. 435 | * 436 | * Requirements: 437 | * 438 | * - `spender` cannot be the zero address. 439 | */ 440 | function approve(address spender, uint256 value) public virtual returns (bool) { 441 | address owner = _msgSender(); 442 | _approve(owner, spender, value); 443 | return true; 444 | } 445 | 446 | /** 447 | * @dev See {IERC20-transferFrom}. 448 | * 449 | * Emits an {Approval} event indicating the updated allowance. This is not 450 | * required by the EIP. See the note at the beginning of {ERC20}. 451 | * 452 | * NOTE: Does not update the allowance if the current allowance 453 | * is the maximum `uint256`. 454 | * 455 | * Requirements: 456 | * 457 | * - `from` and `to` cannot be the zero address. 458 | * - `from` must have a balance of at least `value`. 459 | * - the caller must have allowance for ``from``'s tokens of at least 460 | * `value`. 461 | */ 462 | function transferFrom(address from, address to, uint256 value) public virtual returns (bool) { 463 | address spender = _msgSender(); 464 | _spendAllowance(from, spender, value); 465 | _transfer(from, to, value); 466 | return true; 467 | } 468 | 469 | /** 470 | * @dev Moves a `value` amount of tokens from `from` to `to`. 471 | * 472 | * This internal function is equivalent to {transfer}, and can be used to 473 | * e.g. implement automatic token fees, slashing mechanisms, etc. 474 | * 475 | * Emits a {Transfer} event. 476 | * 477 | * NOTE: This function is not virtual, {_update} should be overridden instead. 478 | */ 479 | function _transfer(address from, address to, uint256 value) internal { 480 | if (from == address(0)) { 481 | revert ERC20InvalidSender(address(0)); 482 | } 483 | if (to == address(0)) { 484 | revert ERC20InvalidReceiver(address(0)); 485 | } 486 | _update(from, to, value); 487 | } 488 | 489 | /** 490 | * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` 491 | * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding 492 | * this function. 493 | * 494 | * Emits a {Transfer} event. 495 | */ 496 | function _update(address from, address to, uint256 value) internal virtual { 497 | if (from == address(0)) { 498 | // Overflow check required: The rest of the code assumes that totalSupply never overflows 499 | _totalSupply += value; 500 | } else { 501 | uint256 fromBalance = _balances[from]; 502 | if (fromBalance < value) { 503 | revert ERC20InsufficientBalance(from, fromBalance, value); 504 | } 505 | unchecked { 506 | // Overflow not possible: value <= fromBalance <= totalSupply. 507 | _balances[from] = fromBalance - value; 508 | } 509 | } 510 | 511 | if (to == address(0)) { 512 | unchecked { 513 | // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply. 514 | _totalSupply -= value; 515 | } 516 | } else { 517 | unchecked { 518 | // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256. 519 | _balances[to] += value; 520 | } 521 | } 522 | 523 | emit Transfer(from, to, value); 524 | } 525 | 526 | /** 527 | * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0). 528 | * Relies on the `_update` mechanism 529 | * 530 | * Emits a {Transfer} event with `from` set to the zero address. 531 | * 532 | * NOTE: This function is not virtual, {_update} should be overridden instead. 533 | */ 534 | function _mint(address account, uint256 value) internal { 535 | if (account == address(0)) { 536 | revert ERC20InvalidReceiver(address(0)); 537 | } 538 | _update(address(0), account, value); 539 | } 540 | 541 | /** 542 | * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply. 543 | * Relies on the `_update` mechanism. 544 | * 545 | * Emits a {Transfer} event with `to` set to the zero address. 546 | * 547 | * NOTE: This function is not virtual, {_update} should be overridden instead 548 | */ 549 | function _burn(address account, uint256 value) internal { 550 | if (account == address(0)) { 551 | revert ERC20InvalidSender(address(0)); 552 | } 553 | _update(account, address(0), value); 554 | } 555 | 556 | /** 557 | * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens. 558 | * 559 | * This internal function is equivalent to `approve`, and can be used to 560 | * e.g. set automatic allowances for certain subsystems, etc. 561 | * 562 | * Emits an {Approval} event. 563 | * 564 | * Requirements: 565 | * 566 | * - `owner` cannot be the zero address. 567 | * - `spender` cannot be the zero address. 568 | * 569 | * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. 570 | */ 571 | function _approve(address owner, address spender, uint256 value) internal { 572 | _approve(owner, spender, value, true); 573 | } 574 | 575 | /** 576 | * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event. 577 | * 578 | * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by 579 | * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any 580 | * `Approval` event during `transferFrom` operations. 581 | * 582 | * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to 583 | * true using the following override: 584 | * ``` 585 | * function _approve(address owner, address spender, uint256 value, bool) internal virtual override { 586 | * super._approve(owner, spender, value, true); 587 | * } 588 | * ``` 589 | * 590 | * Requirements are the same as {_approve}. 591 | */ 592 | function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { 593 | if (owner == address(0)) { 594 | revert ERC20InvalidApprover(address(0)); 595 | } 596 | if (spender == address(0)) { 597 | revert ERC20InvalidSpender(address(0)); 598 | } 599 | _allowances[owner][spender] = value; 600 | if (emitEvent) { 601 | emit Approval(owner, spender, value); 602 | } 603 | } 604 | 605 | /** 606 | * @dev Updates `owner` s allowance for `spender` based on spent `value`. 607 | * 608 | * Does not update the allowance value in case of infinite allowance. 609 | * Revert if not enough allowance is available. 610 | * 611 | * Does not emit an {Approval} event. 612 | */ 613 | function _spendAllowance(address owner, address spender, uint256 value) internal virtual { 614 | uint256 currentAllowance = allowance(owner, spender); 615 | if (currentAllowance != type(uint256).max) { 616 | if (currentAllowance < value) { 617 | revert ERC20InsufficientAllowance(spender, currentAllowance, value); 618 | } 619 | unchecked { 620 | _approve(owner, spender, currentAllowance - value, false); 621 | } 622 | } 623 | } 624 | } 625 | 626 | // File: Challenge-Scroll/ERC20.sol 627 | 628 | 629 | pragma solidity ^0.8.24; 630 | 631 | 632 | // TONCONTRAT ERC20 Token Contract 633 | contract TONCONTRAT is ERC20 { 634 | 635 | // Constructor that mints the initial supply to the deployer's address 636 | constructor(uint256 initialSupply) ERC20("CryptoKVC", "CKVC") { 637 | _mint(msg.sender, initialSupply); // Mint the initial supply of tokens to the deployer's address 638 | } 639 | 640 | // Function to mint new tokens to a specified address 641 | function mint(address to, uint256 amount) public { 642 | _mint(to, amount); // Call internal _mint function to create new tokens 643 | } 644 | 645 | // Function to burn tokens from a specified address 646 | function burn(address from, uint256 amount) public { 647 | _burn(from, amount); // Call internal _burn function to destroy tokens 648 | } 649 | 650 | // Override the transfer function to allow sending tokens 651 | function transfer(address to, uint256 amount) public override returns (bool) { 652 | _transfer(_msgSender(), to, amount); // Call internal _transfer function 653 | return true; // Return success 654 | } 655 | 656 | // Override the approve function to allow an address to spend on behalf of the caller 657 | function approve(address spender, uint256 amount) public override returns (bool) { 658 | _approve(_msgSender(), spender, amount); // Call internal _approve function 659 | return true; // Return success 660 | } 661 | 662 | // Override the transferFrom function to allow spending tokens on behalf of an address 663 | function transferFrom(address from, address to, uint256 amount) public override returns (bool) { 664 | _transfer(from, to, amount); // Transfer tokens from `from` to `to` 665 | uint256 currentAllowance = allowance(from, _msgSender()); 666 | require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance"); 667 | _approve(from, _msgSender(), currentAllowance - amount); // Update allowance 668 | return true; // Return success 669 | } 670 | 671 | // Function to retrieve the balance of a specific address 672 | function getBalanceOf(address account) public view returns (uint256) { 673 | return balanceOf(account); // Call balanceOf to get the balance 674 | } 675 | } 676 | 677 | -------------------------------------------------------------------------------- /Simple Token Swap: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | // File: @uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol 4 | 5 | 6 | pragma solidity >=0.5.0; 7 | 8 | /// @title Callback for IUniswapV3PoolActions#swap 9 | /// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface 10 | interface IUniswapV3SwapCallback { 11 | /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap. 12 | /// @dev In the implementation you must pay the pool tokens owed for the swap. 13 | /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. 14 | /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped. 15 | /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by 16 | /// the end of the swap. If positive, the callback must send that amount of token0 to the pool. 17 | /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by 18 | /// the end of the swap. If positive, the callback must send that amount of token1 to the pool. 19 | /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call 20 | function uniswapV3SwapCallback( 21 | int256 amount0Delta, 22 | int256 amount1Delta, 23 | bytes calldata data 24 | ) external; 25 | } 26 | 27 | // File: @uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol 28 | 29 | 30 | pragma solidity >=0.7.5; 31 | pragma abicoder v2; 32 | 33 | 34 | /// @title Router token swapping functionality 35 | /// @notice Functions for swapping tokens via Uniswap V3 36 | interface ISwapRouter is IUniswapV3SwapCallback { 37 | struct ExactInputSingleParams { 38 | address tokenIn; 39 | address tokenOut; 40 | uint24 fee; 41 | address recipient; 42 | uint256 deadline; 43 | uint256 amountIn; 44 | uint256 amountOutMinimum; 45 | uint160 sqrtPriceLimitX96; 46 | } 47 | 48 | /// @notice Swaps `amountIn` of one token for as much as possible of another token 49 | /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata 50 | /// @return amountOut The amount of the received token 51 | function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut); 52 | 53 | struct ExactInputParams { 54 | bytes path; 55 | address recipient; 56 | uint256 deadline; 57 | uint256 amountIn; 58 | uint256 amountOutMinimum; 59 | } 60 | 61 | /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path 62 | /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata 63 | /// @return amountOut The amount of the received token 64 | function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut); 65 | 66 | struct ExactOutputSingleParams { 67 | address tokenIn; 68 | address tokenOut; 69 | uint24 fee; 70 | address recipient; 71 | uint256 deadline; 72 | uint256 amountOut; 73 | uint256 amountInMaximum; 74 | uint160 sqrtPriceLimitX96; 75 | } 76 | 77 | /// @notice Swaps as little as possible of one token for `amountOut` of another token 78 | /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata 79 | /// @return amountIn The amount of the input token 80 | function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn); 81 | 82 | struct ExactOutputParams { 83 | bytes path; 84 | address recipient; 85 | uint256 deadline; 86 | uint256 amountOut; 87 | uint256 amountInMaximum; 88 | } 89 | 90 | /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed) 91 | /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata 92 | /// @return amountIn The amount of the input token 93 | function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn); 94 | } 95 | 96 | // File: @openzeppelin/contracts/token/ERC20/IERC20.sol 97 | 98 | 99 | // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) 100 | 101 | pragma solidity ^0.8.20; 102 | 103 | /** 104 | * @dev Interface of the ERC20 standard as defined in the EIP. 105 | */ 106 | interface IERC20 { 107 | /** 108 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 109 | * another (`to`). 110 | * 111 | * Note that `value` may be zero. 112 | */ 113 | event Transfer(address indexed from, address indexed to, uint256 value); 114 | 115 | /** 116 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 117 | * a call to {approve}. `value` is the new allowance. 118 | */ 119 | event Approval(address indexed owner, address indexed spender, uint256 value); 120 | 121 | /** 122 | * @dev Returns the value of tokens in existence. 123 | */ 124 | function totalSupply() external view returns (uint256); 125 | 126 | /** 127 | * @dev Returns the value of tokens owned by `account`. 128 | */ 129 | function balanceOf(address account) external view returns (uint256); 130 | 131 | /** 132 | * @dev Moves a `value` amount of tokens from the caller's account to `to`. 133 | * 134 | * Returns a boolean value indicating whether the operation succeeded. 135 | * 136 | * Emits a {Transfer} event. 137 | */ 138 | function transfer(address to, uint256 value) external returns (bool); 139 | 140 | /** 141 | * @dev Returns the remaining number of tokens that `spender` will be 142 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 143 | * zero by default. 144 | * 145 | * This value changes when {approve} or {transferFrom} are called. 146 | */ 147 | function allowance(address owner, address spender) external view returns (uint256); 148 | 149 | /** 150 | * @dev Sets a `value` amount of tokens as the allowance of `spender` over the 151 | * caller's tokens. 152 | * 153 | * Returns a boolean value indicating whether the operation succeeded. 154 | * 155 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 156 | * that someone may use both the old and the new allowance by unfortunate 157 | * transaction ordering. One possible solution to mitigate this race 158 | * condition is to first reduce the spender's allowance to 0 and set the 159 | * desired value afterwards: 160 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 161 | * 162 | * Emits an {Approval} event. 163 | */ 164 | function approve(address spender, uint256 value) external returns (bool); 165 | 166 | /** 167 | * @dev Moves a `value` amount of tokens from `from` to `to` using the 168 | * allowance mechanism. `value` is then deducted from the caller's 169 | * allowance. 170 | * 171 | * Returns a boolean value indicating whether the operation succeeded. 172 | * 173 | * Emits a {Transfer} event. 174 | */ 175 | function transferFrom(address from, address to, uint256 value) external returns (bool); 176 | } 177 | 178 | // File: Challenge-Scroll/Simple Token Swap.sol 179 | 180 | 181 | pragma solidity ^0.8.24; 182 | 183 | // Importing the necessary interfaces and libraries 184 | 185 | 186 | 187 | contract SimpleTokenSwap { 188 | ISwapRouter public swapRouter; 189 | address public WETH; 190 | 191 | // Setting the Uniswap Router address and WETH address in the constructor 192 | constructor(address _swapRouter, address _WETH) { 193 | swapRouter = ISwapRouter(_swapRouter); 194 | WETH = _WETH; 195 | } 196 | 197 | // Create a swap function that takes input and output token addresses, 198 | // the input amount, the minimum output amount, and the recipient's address 199 | function swap( 200 | address tokenIn, 201 | address tokenOut, 202 | uint256 amountIn, 203 | uint256 amountOutMinimum, 204 | address recipient 205 | ) external { 206 | // Transfer the input tokens from the sender to the contract 207 | require(IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn), "Transfer failed"); 208 | 209 | // Approve the Uniswap router to spend the input tokens 210 | require(IERC20(tokenIn).approve(address(swapRouter), amountIn), "Approval failed"); 211 | 212 | // Create the swap parameters 213 | ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({ 214 | tokenIn: tokenIn, 215 | tokenOut: tokenOut, 216 | fee: 3000, // Using a 0.3% fee tier 217 | recipient: recipient, 218 | deadline: block.timestamp, 219 | amountIn: amountIn, 220 | amountOutMinimum: amountOutMinimum, 221 | sqrtPriceLimitX96: 0 222 | }); 223 | 224 | // Call the Uniswap router's exactInputSingle function to execute the swap 225 | uint256 amountOut = swapRouter.exactInputSingle(params); 226 | 227 | require(amountOut >= amountOutMinimum, "Insufficient output amount"); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import { config as dotenv } from "dotenv"; 2 | import { 3 | createWalletClient, 4 | http, 5 | getContract, 6 | erc20Abi, 7 | parseUnits, 8 | maxUint256, 9 | publicActions, 10 | concat, 11 | numberToHex, 12 | size, 13 | } from "viem"; 14 | import type { Hex } from "viem"; 15 | import { privateKeyToAccount } from "viem/accounts"; 16 | import { scroll } from "viem/chains"; 17 | import { wethAbi } from "./abi/weth-abi"; 18 | 19 | /* For the 0x Challenge on Scroll, implement the following 20 | 21 | 1. Display the percentage breakdown of liquidity sources 22 | 2. Monetize your app with affiliate fees and surplus collection 23 | 3. Display buy/sell tax for tokens with tax 24 | 4. Display all sources of liquidity on Scroll 25 | 26 | */ 27 | 28 | const qs = require("qs"); 29 | 30 | // Load environment variables 31 | dotenv(); 32 | const { PRIVATE_KEY, ZERO_EX_API_KEY, ALCHEMY_HTTP_TRANSPORT_URL } = 33 | process.env; 34 | 35 | // Validate requirements 36 | if (!PRIVATE_KEY) throw new Error("missing PRIVATE_KEY."); 37 | if (!ZERO_EX_API_KEY) throw new Error("missing ZERO_EX_API_KEY."); 38 | if (!ALCHEMY_HTTP_TRANSPORT_URL) 39 | throw new Error("missing ALCHEMY_HTTP_TRANSPORT_URL."); 40 | 41 | // Fetch headers 42 | const headers = new Headers({ 43 | "Content-Type": "application/json", 44 | "0x-api-key": ZERO_EX_API_KEY, 45 | "0x-version": "v2", 46 | }); 47 | 48 | // Setup wallet client 49 | const client = createWalletClient({ 50 | account: privateKeyToAccount(`0x${PRIVATE_KEY}` as `0x${string}`), 51 | chain: scroll, 52 | transport: http(ALCHEMY_HTTP_TRANSPORT_URL), 53 | }).extend(publicActions); // Extend wallet client with publicActions for public client 54 | 55 | const [address] = await client.getAddresses(); 56 | 57 | // Set up contracts 58 | const weth = getContract({ 59 | address: "0x5300000000000000000000000000000000000004", 60 | abi: wethAbi, 61 | client, 62 | }); 63 | const wsteth = getContract({ 64 | address: "0xf610A9dfB7C89644979b4A0f27063E9e7d7Cda32", 65 | abi: erc20Abi, 66 | client, 67 | }); 68 | 69 | // Function to display the percentage breakdown of liquidity sources 70 | function displayLiquiditySources(route: any) { 71 | const fills = route.fills; 72 | const totalBps = fills.reduce((acc: number, fill: any) => acc + parseInt(fill.proportionBps), 0); 73 | 74 | console.log(`${fills.length} Sources`); 75 | fills.forEach((fill: any) => { 76 | const percentage = (parseInt(fill.proportionBps) / 100).toFixed(2); 77 | console.log(`${fill.source}: ${percentage}%`); 78 | }); 79 | } 80 | 81 | // Function to display the buy/sell taxes for tokens 82 | function displayTokenTaxes(tokenMetadata: any) { 83 | const buyTokenBuyTax = (parseInt(tokenMetadata.buyToken.buyTaxBps) / 100).toFixed(2); 84 | const buyTokenSellTax = (parseInt(tokenMetadata.buyToken.sellTaxBps) / 100).toFixed(2); 85 | const sellTokenBuyTax = (parseInt(tokenMetadata.sellToken.buyTaxBps) / 100).toFixed(2); 86 | const sellTokenSellTax = (parseInt(tokenMetadata.sellToken.sellTaxBps) / 100).toFixed(2); 87 | 88 | if (buyTokenBuyTax > 0 || buyTokenSellTax > 0) { 89 | console.log(`Buy Token Buy Tax: ${buyTokenBuyTax}%`); 90 | console.log(`Buy Token Sell Tax: ${buyTokenSellTax}%`); 91 | } 92 | 93 | if (sellTokenBuyTax > 0 || sellTokenSellTax > 0) { 94 | console.log(`Sell Token Buy Tax: ${sellTokenBuyTax}%`); 95 | console.log(`Sell Token Sell Tax: ${sellTokenSellTax}%`); 96 | } 97 | } 98 | 99 | // Function to display all liquidity sources on Scroll 100 | const getLiquiditySources = async () => { 101 | const chainId = client.chain.id.toString(); // Ensure this is the correct ID for Scroll 102 | const sourcesParams = new URLSearchParams({ 103 | chainId: chainId, 104 | }); 105 | 106 | const sourcesResponse = await fetch( 107 | `https://api.0x.org/swap/v1/sources?${sourcesParams.toString()}`, 108 | { 109 | headers, 110 | } 111 | ); 112 | 113 | const sourcesData = await sourcesResponse.json(); 114 | const sources = Object.keys(sourcesData.sources); 115 | console.log("Liquidity sources for Scroll chain:"); 116 | console.log(sources.join(", ")); 117 | }; 118 | 119 | const main = async () => { 120 | // 4. Display all liquidity sources on Scroll 121 | await getLiquiditySources(); 122 | 123 | // Specify sell amount 124 | const decimals = (await weth.read.decimals()) as number; 125 | const sellAmount = parseUnits("0.1", decimals); 126 | 127 | // 2. Add parameters for affiliate fees and surplus collection 128 | const affiliateFeeBps = "100"; // 1% 129 | const surplusCollection = "true"; 130 | 131 | // 1. Fetch price with monetization parameters 132 | const priceParams = new URLSearchParams({ 133 | chainId: client.chain.id.toString(), 134 | sellToken: weth.address, 135 | buyToken: wsteth.address, 136 | sellAmount: sellAmount.toString(), 137 | taker: client.account.address, 138 | affiliateFee: affiliateFeeBps, // Parameter for affiliate fees 139 | surplusCollection: surplusCollection, // Parameter for surplus collection 140 | }); 141 | 142 | const priceResponse = await fetch( 143 | "https://api.0x.org/swap/permit2/price?" + priceParams.toString(), 144 | { 145 | headers, 146 | } 147 | ); 148 | 149 | const price = await priceResponse.json(); 150 | console.log("Fetching price to swap 0.1 WETH for wstETH"); 151 | console.log( 152 | `https://api.0x.org/swap/permit2/price?${priceParams.toString()}` 153 | ); 154 | console.log("priceResponse: ", price); 155 | 156 | // 2. Check if taker needs to set an allowance for Permit2 157 | if (price.issues.allowance !== null) { 158 | try { 159 | const { request } = await weth.simulate.approve([ 160 | price.issues.allowance.spender, 161 | maxUint256, 162 | ]); 163 | console.log("Approving Permit2 to spend WETH...", request); 164 | // Set approval 165 | const hash = await weth.write.approve(request.args); 166 | console.log( 167 | "Approved Permit2 to spend WETH.", 168 | await client.waitForTransactionReceipt({ hash }) 169 | ); 170 | } catch (error) { 171 | console.log("Error approving Permit2:", error); 172 | } 173 | } else { 174 | console.log("WETH already approved for Permit2"); 175 | } 176 | 177 | // 3. Fetch quote with monetization parameters 178 | const quoteParams = new URLSearchParams(); 179 | for (const [key, value] of priceParams.entries()) { 180 | quoteParams.append(key, value); 181 | } 182 | 183 | const quoteResponse = await fetch( 184 | "https://api.0x.org/swap/permit2/quote?" + quoteParams.toString(), 185 | { 186 | headers, 187 | } 188 | ); 189 | 190 | const quote = await quoteResponse.json(); 191 | console.log("Fetching quote to swap 0.1 WETH for wstETH"); 192 | console.log("quoteResponse: ", quote); 193 | 194 | // 1. Display the percentage breakdown of liquidity sources 195 | if (quote.route) { 196 | displayLiquiditySources(quote.route); 197 | } 198 | 199 | // 3. Display the buy/sell taxes for tokens 200 | if (quote.tokenMetadata) { 201 | displayTokenTaxes(quote.tokenMetadata); 202 | } 203 | 204 | // 2. Display monetization information 205 | if (quote.affiliateFeeBps) { 206 | const affiliateFee = (parseInt(quote.affiliateFeeBps) / 100).toFixed(2); 207 | console.log(`Affiliate Fee: ${affiliateFee}%`); 208 | } 209 | 210 | if (quote.tradeSurplus && parseFloat(quote.tradeSurplus) > 0) { 211 | console.log(`Trade Surplus Collected: ${quote.tradeSurplus}`); 212 | } 213 | 214 | // 4. Sign permit2.eip712 returned from quote 215 | let signature: Hex | undefined; 216 | if (quote.permit2?.eip712) { 217 | try { 218 | signature = await client.signTypedData(quote.permit2.eip712); 219 | console.log("Signed permit2 message from quote response"); 220 | } catch (error) { 221 | console.error("Error signing permit2 coupon:", error); 222 | } 223 | 224 | // 5. Append sig length and sig data to transaction.data 225 | if (signature && quote?.transaction?.data) { 226 | const signatureLengthInHex = numberToHex(size(signature), { 227 | signed: false, 228 | size: 32, 229 | }); 230 | 231 | const transactionData = quote.transaction.data as Hex; 232 | const sigLengthHex = signatureLengthInHex as Hex; 233 | const sig = signature as Hex; 234 | 235 | quote.transaction.data = concat([transactionData, sigLengthHex, sig]); 236 | } else { 237 | throw new Error("Failed to obtain signature or transaction data"); 238 | } 239 | } 240 | 241 | // 6. Submit transaction with permit2 signature 242 | if (signature && quote.transaction.data) { 243 | const nonce = await client.getTransactionCount({ 244 | address: client.account.address, 245 | }); 246 | 247 | const signedTransaction = await client.signTransaction({ 248 | account: client.account, 249 | chain: client.chain, 250 | gas: quote?.transaction.gas ? BigInt(quote.transaction.gas) : undefined, 251 | to: quote?.transaction.to, 252 | data: quote.transaction.data, 253 | value: quote?.transaction.value 254 | ? BigInt(quote.transaction.value) 255 | : undefined, // value is used for native tokens 256 | gasPrice: quote?.transaction.gasPrice 257 | ? BigInt(quote.transaction.gasPrice) 258 | : undefined, 259 | nonce: nonce, 260 | }); 261 | const hash = await client.sendRawTransaction({ 262 | serializedTransaction: signedTransaction, 263 | }); 264 | 265 | console.log("Transaction hash:", hash); 266 | 267 | console.log(`See tx details at https://scrollscan.com/tx/${hash}`); 268 | } else { 269 | console.error("Failed to obtain a signature, transaction not sent."); 270 | } 271 | }; 272 | 273 | main(); 274 | --------------------------------------------------------------------------------