Repository: ava-labs/avalanche-smart-contract-quickstart Branch: main Commit: 3ad93abf50fb Files: 20 Total size: 41.2 KB Directory structure: gitextract_1nu2uio7/ ├── .gitignore ├── LICENSE ├── README.md ├── contracts/ │ ├── ExampleERC20.sol │ ├── MockContract.sol │ ├── NFT.sol │ └── Storage.sol ├── hardhat.config.ts ├── package.json ├── scripts/ │ ├── deploy.ts │ ├── erc20.ts │ ├── example.js │ ├── fund-cchain-addresses.js │ ├── nft.ts │ ├── sendAvaxJSONProvider.ts │ ├── sendAvaxWalletSigner.ts │ └── storage.ts ├── setup.js ├── test/ │ └── Coin.js └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ /dist /.idea *.tsbuildinfo .DS_Store # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* node_modules/ .env* !.env*.default .vscode/* !.vscode/settings.json.default cache/ artifacts/ .yalc yalc.lock # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # Snowpack dependency directory (https://snowpack.dev/) web_modules/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env .env.test .env.production # parcel-bundler cache (https://parceljs.org/) .cache .parcel-cache # Next.js build output .next out # Nuxt.js build / generate output .nuxt dist # Gatsby files .cache/ # Comment in the public line in if your project uses Gatsby and not Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port # Stores VSCode versions used for testing VSCode extensions .vscode-test # yarn v2 .yarn/cache .yarn/unplugged .yarn/build-state.yml .yarn/install-state.gz .pnp.* ================================================ FILE: LICENSE ================================================ BSD 3-Clause License Copyright (c) 2020, Ava Labs, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.md ================================================ ## Introduction Avalanche is an open-source platform for launching decentralized applications and enterprise blockchain deployments in one interoperable, highly scalable ecosystem. Avalanche gives you complete control on both the network and application layers—helping you build anything you can imagine. The Avalanche Network is composed of many blockchains. One of these blockchains is the C-Chain (Contract Chain), which is an Ethereum Virtual Machine instance. The C-Chain's API is almost identical to an Ethereum node's API. Avalanche offers the same interface as Ethereum but with higher speed, higher throughput, lower fees and lower transaction confirmation times. These properties considerably improve the performance of DApps and the user experience of smart contracts. The goal of this guide is to lay out best practices regarding writing, testing and deployment of smart contracts to Avalanche's C-Chain. We'll be building smart contracts with development environment [Hardhat](https://hardhat.org). ## Prerequisites ### NodeJS and Yarn First, install the LTS (long-term support) version of [nodejs](https://nodejs.org/en). This is `14.17.0` at the time of writing. NodeJS bundles `npm`. Next, install [yarn](https://yarnpkg.com): ```zsh npm install -g yarn ``` ### AvalancheGo and Avash [AvalancheGo](https://github.com/ava-labs/avalanchego) is an Avalanche node implementation written in Go. [Avash](https://docs.avax.network/build/tools/avash) is a tool to quickly deploy local test networks. Together, you can deploy local test networks and run tests on them. ### Solidity and Avalanche It is also helpful to have a basic understanding of [Solidity](https://docs.soliditylang.org) and [Avalanche](https://docs.avax.network). ## Dependencies Clone the [quickstart repository](https://github.com/ava-labs/avalanche-smart-contract-quickstart) and install the necessary packages via `yarn`. ```zsh $ git clone https://github.com/ava-labs/avalanche-smart-contract-quickstart.git $ cd avalanche-smart-contract-quickstart $ yarn ``` ## Write Contracts Edit the `Coin.sol` contract in `contracts/`. `Coin.sol` is an [Open Zeppelin](https://openzeppelin.com) [ERC20](https://eips.ethereum.org/EIPS/eip-20) contract. ERC20 is a popular smart contract interface. You can also add your own contracts. ## Hardhat Config Hardhat uses `hardhat.config.js` as the configuration file. You can define tasks, networks, compilers and more in that file. For more information see [here](https://hardhat.org/config/). In our repository we use a pre-configured file [hardhat.config.ts](https://github.com/ava-labs/avalanche-smart-contract-quickstart/blob/main/hardhat.config.ts). This file configures necessary network information to provide smooth interaction with Avalanche. There are also some pre-defined private keys for testing on a local test network. ## Hardhat Tasks You can define custom hardhat tasks in [hardhat.config.ts](https://github.com/ava-labs/avalanche-smart-contract-quickstart/blob/main/hardhat.config.ts). ## Documentation There is a documentation under the Avalanche's official documentation repository: [Using Hardhat with the Avalanche C-Chain](https://docs.avax.network/build/tutorials/smart-contracts/using-hardhat-with-the-avalanche-c-chain) ================================================ FILE: contracts/ExampleERC20.sol ================================================ //SPDX-License-Identifier: MIT pragma solidity >=0.6.2; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract ExampleERC20 is ERC20, Ownable { string private TOKEN_NAME = "Example ERC20 Token"; string private TOKEN_SYMBOL = "XMPL"; uint256 private constant TOTAL_SUPPLY = 123456789; constructor()ERC20(TOKEN_NAME, TOKEN_SYMBOL) { _mint(msg.sender, TOTAL_SUPPLY); } function mint(address to, uint256 amount) public onlyOwner { _mint(to, amount); } function burn(address from, uint256 amount) public onlyOwner { _burn(from, amount); } } ================================================ FILE: contracts/MockContract.sol ================================================ //SPDX-License-Identifier: MIT pragma solidity ^0.6.4; /////////////////////////////////////////////// // This contract is taken from Gnosis and is // used to facilitate unit testing. It should // not be deployed in production. /////////////////////////////////////////////// interface MockInterface { /** * @dev After calling this method, the mock will return `response` when it is called * with any calldata that is not mocked more specifically below * (e.g. using givenMethodReturn). * @param response ABI encoded response that will be returned if method is invoked */ function givenAnyReturn(bytes calldata response) external; function givenAnyReturnBool(bool response) external; function givenAnyReturnUint(uint256 response) external; function givenAnyReturnAddress(address response) external; function givenAnyRevert() external; function givenAnyRevertWithMessage(string calldata message) external; function givenAnyRunOutOfGas() external; /** * @dev After calling this method, the mock will return `response` when the given * methodId is called regardless of arguments. If the methodId and arguments * are mocked more specifically (using `givenMethodAndArguments`) the latter * will take precedence. * @param method ABI encoded methodId. It is valid to pass full calldata (including arguments). The mock will extract the methodId from it * @param response ABI encoded response that will be returned if method is invoked */ function givenMethodReturn(bytes calldata method, bytes calldata response) external; function givenMethodReturnBool(bytes calldata method, bool response) external; function givenMethodReturnUint(bytes calldata method, uint256 response) external; function givenMethodReturnAddress(bytes calldata method, address response) external; function givenMethodRevert(bytes calldata method) external; function givenMethodRevertWithMessage( bytes calldata method, string calldata message ) external; function givenMethodRunOutOfGas(bytes calldata method) external; /** * @dev After calling this method, the mock will return `response` when the given * methodId is called with matching arguments. These exact calldataMocks will take * precedence over all other calldataMocks. * @param call ABI encoded calldata (methodId and arguments) * @param response ABI encoded response that will be returned if contract is invoked with calldata */ function givenCalldataReturn(bytes calldata call, bytes calldata response) external; function givenCalldataReturnBool(bytes calldata call, bool response) external; function givenCalldataReturnUint(bytes calldata call, uint256 response) external; function givenCalldataReturnAddress(bytes calldata call, address response) external; function givenCalldataRevert(bytes calldata call) external; function givenCalldataRevertWithMessage( bytes calldata call, string calldata message ) external; function givenCalldataRunOutOfGas(bytes calldata call) external; /** * @dev Returns the number of times anything has been called on this mock since last reset */ function invocationCount() external returns (uint256); /** * @dev Returns the number of times the given method has been called on this mock since last reset * @param method ABI encoded methodId. It is valid to pass full calldata (including arguments). The mock will extract the methodId from it */ function invocationCountForMethod(bytes calldata method) external returns (uint256); /** * @dev Returns the number of times this mock has been called with the exact calldata since last reset. * @param call ABI encoded calldata (methodId and arguments) */ function invocationCountForCalldata(bytes calldata call) external returns (uint256); /** * @dev Resets all mocked methods and invocation counts. */ function reset() external; } /** * Implementation of the MockInterface. */ contract MockContract is MockInterface { enum MockType { Return, Revert, OutOfGas } bytes32 public constant MOCKS_LIST_START = hex"01"; bytes public constant MOCKS_LIST_END = "0xff"; bytes32 public constant MOCKS_LIST_END_HASH = keccak256(MOCKS_LIST_END); bytes4 public constant SENTINEL_ANY_MOCKS = hex"01"; bytes public constant DEFAULT_FALLBACK_VALUE = abi.encode(false); // A linked list allows easy iteration and inclusion checks mapping(bytes32 => bytes) calldataMocks; mapping(bytes => MockType) calldataMockTypes; mapping(bytes => bytes) calldataExpectations; mapping(bytes => string) calldataRevertMessage; mapping(bytes32 => uint256) calldataInvocations; mapping(bytes4 => bytes4) methodIdMocks; mapping(bytes4 => MockType) methodIdMockTypes; mapping(bytes4 => bytes) methodIdExpectations; mapping(bytes4 => string) methodIdRevertMessages; mapping(bytes32 => uint256) methodIdInvocations; MockType fallbackMockType; bytes fallbackExpectation = DEFAULT_FALLBACK_VALUE; string fallbackRevertMessage; uint256 invocations; uint256 resetCount; constructor() public { calldataMocks[MOCKS_LIST_START] = MOCKS_LIST_END; methodIdMocks[SENTINEL_ANY_MOCKS] = SENTINEL_ANY_MOCKS; } function trackCalldataMock(bytes memory call) private { bytes32 callHash = keccak256(call); if (calldataMocks[callHash].length == 0) { calldataMocks[callHash] = calldataMocks[MOCKS_LIST_START]; calldataMocks[MOCKS_LIST_START] = call; } } function trackMethodIdMock(bytes4 methodId) private { if (methodIdMocks[methodId] == 0x0) { methodIdMocks[methodId] = methodIdMocks[SENTINEL_ANY_MOCKS]; methodIdMocks[SENTINEL_ANY_MOCKS] = methodId; } } function _givenAnyReturn(bytes memory response) internal { fallbackMockType = MockType.Return; fallbackExpectation = response; } function givenAnyReturn(bytes calldata response) external override { _givenAnyReturn(response); } function givenAnyReturnBool(bool response) external override { uint256 flag = response ? 1 : 0; _givenAnyReturn(uintToBytes(flag)); } function givenAnyReturnUint(uint256 response) external override { _givenAnyReturn(uintToBytes(response)); } function givenAnyReturnAddress(address response) external override { _givenAnyReturn(uintToBytes(uint256(response))); } function givenAnyRevert() external override { fallbackMockType = MockType.Revert; fallbackRevertMessage = ""; } function givenAnyRevertWithMessage(string calldata message) external override { fallbackMockType = MockType.Revert; fallbackRevertMessage = message; } function givenAnyRunOutOfGas() external override { fallbackMockType = MockType.OutOfGas; } function _givenCalldataReturn(bytes memory call, bytes memory response) private { calldataMockTypes[call] = MockType.Return; calldataExpectations[call] = response; trackCalldataMock(call); } function givenCalldataReturn(bytes calldata call, bytes calldata response) external override { _givenCalldataReturn(call, response); } function givenCalldataReturnBool(bytes calldata call, bool response) external override { uint256 flag = response ? 1 : 0; _givenCalldataReturn(call, uintToBytes(flag)); } function givenCalldataReturnUint(bytes calldata call, uint256 response) external override { _givenCalldataReturn(call, uintToBytes(response)); } function givenCalldataReturnAddress(bytes calldata call, address response) external override { _givenCalldataReturn(call, uintToBytes(uint256(response))); } function _givenMethodReturn(bytes memory call, bytes memory response) private { bytes4 method = bytesToBytes4(call); methodIdMockTypes[method] = MockType.Return; methodIdExpectations[method] = response; trackMethodIdMock(method); } function givenMethodReturn(bytes calldata call, bytes calldata response) external override { _givenMethodReturn(call, response); } function givenMethodReturnBool(bytes calldata call, bool response) external override { uint256 flag = response ? 1 : 0; _givenMethodReturn(call, uintToBytes(flag)); } function givenMethodReturnUint(bytes calldata call, uint256 response) external override { _givenMethodReturn(call, uintToBytes(response)); } function givenMethodReturnAddress(bytes calldata call, address response) external override { _givenMethodReturn(call, uintToBytes(uint256(response))); } function givenCalldataRevert(bytes calldata call) external override { calldataMockTypes[call] = MockType.Revert; calldataRevertMessage[call] = ""; trackCalldataMock(call); } function givenMethodRevert(bytes calldata call) external override { bytes4 method = bytesToBytes4(call); methodIdMockTypes[method] = MockType.Revert; trackMethodIdMock(method); } function givenCalldataRevertWithMessage( bytes calldata call, string calldata message ) external override { calldataMockTypes[call] = MockType.Revert; calldataRevertMessage[call] = message; trackCalldataMock(call); } function givenMethodRevertWithMessage( bytes calldata call, string calldata message ) external override { bytes4 method = bytesToBytes4(call); methodIdMockTypes[method] = MockType.Revert; methodIdRevertMessages[method] = message; trackMethodIdMock(method); } function givenCalldataRunOutOfGas(bytes calldata call) external override { calldataMockTypes[call] = MockType.OutOfGas; trackCalldataMock(call); } function givenMethodRunOutOfGas(bytes calldata call) external override { bytes4 method = bytesToBytes4(call); methodIdMockTypes[method] = MockType.OutOfGas; trackMethodIdMock(method); } function invocationCount() external override returns (uint256) { return invocations; } function invocationCountForMethod(bytes calldata call) external override returns (uint256) { bytes4 method = bytesToBytes4(call); return methodIdInvocations[keccak256(abi.encodePacked(resetCount, method))]; } function invocationCountForCalldata(bytes calldata call) external override returns (uint256) { return calldataInvocations[keccak256(abi.encodePacked(resetCount, call))]; } function reset() external override { // Reset all exact calldataMocks bytes memory nextMock = calldataMocks[MOCKS_LIST_START]; bytes32 mockHash = keccak256(nextMock); // We cannot compary bytes while (mockHash != MOCKS_LIST_END_HASH) { // Reset all mock maps calldataMockTypes[nextMock] = MockType.Return; calldataExpectations[nextMock] = hex""; calldataRevertMessage[nextMock] = ""; // Set next mock to remove nextMock = calldataMocks[mockHash]; // Remove from linked list calldataMocks[mockHash] = ""; // Update mock hash mockHash = keccak256(nextMock); } // Clear list calldataMocks[MOCKS_LIST_START] = MOCKS_LIST_END; // Reset all any calldataMocks bytes4 nextAnyMock = methodIdMocks[SENTINEL_ANY_MOCKS]; while (nextAnyMock != SENTINEL_ANY_MOCKS) { bytes4 currentAnyMock = nextAnyMock; methodIdMockTypes[currentAnyMock] = MockType.Return; methodIdExpectations[currentAnyMock] = hex""; methodIdRevertMessages[currentAnyMock] = ""; nextAnyMock = methodIdMocks[currentAnyMock]; // Remove from linked list methodIdMocks[currentAnyMock] = 0x0; } // Clear list methodIdMocks[SENTINEL_ANY_MOCKS] = SENTINEL_ANY_MOCKS; fallbackExpectation = DEFAULT_FALLBACK_VALUE; fallbackMockType = MockType.Return; invocations = 0; resetCount += 1; } function useAllGas() private { while (true) { bool s; assembly { //expensive call to EC multiply contract s := call(sub(gas(), 2000), 6, 0, 0x0, 0xc0, 0x0, 0x60) } } } function bytesToBytes4(bytes memory b) private pure returns (bytes4) { bytes4 out; for (uint256 i = 0; i < 4; i++) { out |= bytes4(b[i] & 0xFF) >> (i * 8); } return out; } function uintToBytes(uint256 x) private pure returns (bytes memory b) { b = new bytes(32); assembly { mstore(add(b, 32), x) } } function updateInvocationCount(bytes4 methodId, bytes memory originalMsgData) public { require( msg.sender == address(this), "Can only be called from the contract itself" ); invocations += 1; methodIdInvocations[keccak256(abi.encodePacked(resetCount, methodId))] += 1; calldataInvocations[ keccak256(abi.encodePacked(resetCount, originalMsgData)) ] += 1; } fallback() external payable { bytes4 methodId; assembly { methodId := calldataload(0) } // First, check exact matching overrides if (calldataMockTypes[msg.data] == MockType.Revert) { revert(calldataRevertMessage[msg.data]); } if (calldataMockTypes[msg.data] == MockType.OutOfGas) { useAllGas(); } bytes memory result = calldataExpectations[msg.data]; // Then check method Id overrides if (result.length == 0) { if (methodIdMockTypes[methodId] == MockType.Revert) { revert(methodIdRevertMessages[methodId]); } if (methodIdMockTypes[methodId] == MockType.OutOfGas) { useAllGas(); } result = methodIdExpectations[methodId]; } // Last, use the fallback override if (result.length == 0) { if (fallbackMockType == MockType.Revert) { revert(fallbackRevertMessage); } if (fallbackMockType == MockType.OutOfGas) { useAllGas(); } result = fallbackExpectation; } // Record invocation as separate call so we don't rollback in case we are called with STATICCALL (, bytes memory r) = address(this).call{ gas: 100000 }( abi.encodeWithSignature( "updateInvocationCount(bytes4,bytes)", methodId, msg.data ) ); assert(r.length == 0); assembly { return(add(0x20, result), mload(result)) } } } ================================================ FILE: contracts/NFT.sol ================================================ //SPDX-License-Identifier: MIT // contracts/ERC721.sol pragma solidity >=0.6.2; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract NFT is ERC721 { using Counters for Counters.Counter; Counters.Counter private _tokenIds; constructor() ERC721("GameItem", "ITM") {} // commented out unused variable // function awardItem(address player, string memory tokenURI) function awardItem(address player) public returns (uint256) { _tokenIds.increment(); uint256 newItemId = _tokenIds.current(); _mint(player, newItemId); // _setTokenURI(newItemId, tokenURI); return newItemId; } } ================================================ FILE: contracts/Storage.sol ================================================ //SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; contract Storage { uint256 number; function store(uint256 num) public { number = num; } function retrieve() public view returns (uint256) { return number; } } ================================================ FILE: hardhat.config.ts ================================================ import { task } from "hardhat/config" import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" import { BigNumber } from "ethers" import "@nomiclabs/hardhat-waffle" // When using the hardhat network, you may choose to fork Fuji or Avalanche Mainnet // This will allow you to debug contracts using the hardhat network while keeping the current network state // To enable forking, turn one of these booleans on, and then run your tasks/scripts using ``--network hardhat`` // For more information go to the hardhat guide // https://hardhat.org/hardhat-network/ // https://hardhat.org/guides/mainnet-forking.html const FORK_FUJI = false const FORK_MAINNET = false const forkingData = FORK_FUJI ? { url: 'https://api.avax-test.network/ext/bc/C/rpc', } : FORK_MAINNET ? { url: 'https://api.avax.network/ext/bc/C/rpc' } : undefined task("accounts", "Prints the list of accounts", async (args, hre): Promise => { const accounts: SignerWithAddress[] = await hre.ethers.getSigners() accounts.forEach((account: SignerWithAddress): void => { console.log(account.address) }) }) task("balances", "Prints the list of AVAX account balances", async (args, hre): Promise => { const accounts: SignerWithAddress[] = await hre.ethers.getSigners() for(const account of accounts){ const balance: BigNumber = await hre.ethers.provider.getBalance( account.address ); console.log(`${account.address} has balance ${balance.toString()}`); } }) export default { solidity: { compilers: [ { version: "0.5.16" }, { version: "0.6.2" }, { version: "0.6.4" }, { version: "0.7.0" }, { version: "0.8.0" } ] }, networks: { hardhat: { gasPrice: 225000000000, chainId: !forkingData ? 43112 : undefined, //Only specify a chainId if we are not forking forking: forkingData }, local: { url: 'http://localhost:9650/ext/bc/C/rpc', gasPrice: 225000000000, chainId: 43112, accounts: [ "0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027", "0x7b4198529994b0dc604278c99d153cfd069d594753d471171a1d102a10438e07", "0x15614556be13730e9e8d6eacc1603143e7b96987429df8726384c2ec4502ef6e", "0x31b571bf6894a248831ff937bb49f7754509fe93bbd2517c9c73c4144c0e97dc", "0x6934bef917e01692b789da754a0eae31a8536eb465e7bff752ea291dad88c675", "0xe700bdbdbc279b808b1ec45f8c2370e4616d3a02c336e68d85d4668e08f53cff", "0xbbc2865b76ba28016bc2255c7504d000e046ae01934b04c694592a6276988630", "0xcdbfd34f687ced8c6968854f8a99ae47712c4f4183b78dcc4a903d1bfe8cbf60", "0x86f78c5416151fe3546dece84fda4b4b1e36089f2dbc48496faf3a950f16157c", "0x750839e9dbbd2a0910efe40f50b2f3b2f2f59f5580bb4b83bd8c1201cf9a010a" ] }, fuji: { url: 'https://api.avax-test.network/ext/bc/C/rpc', gasPrice: 225000000000, chainId: 43113, accounts: [] }, mainnet: { url: 'https://api.avax.network/ext/bc/C/rpc', gasPrice: 225000000000, chainId: 43114, accounts: [] } } } ================================================ FILE: package.json ================================================ { "name": "avalanche-smart-contract-quickstart", "devDependencies": { "@nomiclabs/hardhat-ethers": "^2.0.2", "@nomiclabs/hardhat-waffle": "^2.0.1", "@openzeppelin/contracts": "^4.3.0", "@types/chai": "^4.2.21", "@types/mocha": "^9.0.0", "@types/node": "^16.7.1", "avalanche": "3.8.5", "chai": "^4.3.4", "ethereum-waffle": "^3.4.0", "ethereumjs-tx": "^2.1.2", "ethers": "^5.4.5", "hardhat": "2.6.1", "ts-node": "^10.2.1", "web3": "^1.5.2" }, "version": "1.0.0", "description": "", "main": "index.js", "repository": "https://github.com/ava-labs/avalanche-smart-contract-quickstart", "author": "Connor Daly ", "contributors": [ "Gabriel Cardona " ], "license": "BSD-3-Clause", "scripts": { "precompile": "rimraf ./build/", "compile": "npx hardhat compile", "console": "npx hardhat console", "pretest": "yarn compile", "test": "npx hardhat test", "deploy": "npx hardhat run scripts/deploy.ts", "erc20": "npx hardhat run scripts/erc20.ts", "nft": "npx hardhat run scripts/nft.ts --network mainnet", "storage": "npx hardhat run scripts/storage.ts", "send-avax-wallet-signer": "npx hardhat run scripts/sendAvaxWalletSigner.ts", "send-avax-json-provider": "npx hardhat run scripts/sendAvaxJSONProvider.ts", "lint": "prettier ./test/**/*.ts --check", "prepublishOnly": "yarn test", "hardhat": "npx hardhat", "accounts": "npx hardhat accounts", "balances": "npx hardhat balances", "fund-cchain-addresses": "npx hardhat run scripts/fund-cchain-addresses.js" }, "dependencies": { "typescript": "^4.5.4" }, "engines": { "node": ">=14.17.0" } } ================================================ FILE: scripts/deploy.ts ================================================ import { Contract, ContractFactory } from "ethers" import { ethers } from "hardhat" const main = async(): Promise => { const Coin: ContractFactory = await ethers.getContractFactory("ExampleERC20") const coin: Contract = await Coin.deploy() await coin.deployed() console.log(`Coin deployed to: ${coin.address}`) } main() .then(() => process.exit(0)) .catch(error => { console.error(error) process.exit(1) }) ================================================ FILE: scripts/erc20.ts ================================================ import { BigNumber, Contract } from "ethers" import { ethers } from "hardhat" const coinName: string = "" const coinAddr: string = "" const walletAddress: string = "" const main = async (): Promise => { const contract: Contract = await ethers.getContractAt(coinName, coinAddr) const contractAddress: string = contract.address console.log(`Address: ${contractAddress}`) const name: string = await contract.name() console.log(`Name: ${name}`) const symbol: string = await contract.symbol() console.log(`Symbol: ${symbol}`) const decimals: string = await contract.decimals() console.log(`Decimals: ${decimals}`) const balance: BigNumber = await contract.balanceOf(walletAddress) console.log(`Balance of ${walletAddress}: ${balance.toString()}`) let totalSupply: BigNumber = await contract.totalSupply() console.log(`Total supply: ${totalSupply.toString()}`) console.log(`-----MINTING-----`) await contract.mint(walletAddress, totalSupply) totalSupply = await contract.totalSupply() console.log(`Total supply: ${totalSupply.toString()}`) console.log(`-----BURNING-----`) await contract.burn(walletAddress, totalSupply) totalSupply = await contract.totalSupply() console.log(`Total supply: ${totalSupply.toString()}`) const tx = await contract.transfer(walletAddress, balance) console.log("--TX--") console.log(tx) const txReceipt = await tx.wait() console.log("--TX RECEIPT--") console.log(txReceipt) } main() .then(() => process.exit(0)) .catch(error => { console.error(error) process.exit(1) }) ================================================ FILE: scripts/example.js ================================================ const { Contract } = require("ethers"); const hre = require("hardhat"); // TODO: Enter your deployed contract address const COIN_ADDR = "0x0000000000000000000000000000000000000000"; /** * Empty. Try calling some functions here. */ async function main() { } /** * Takes a transaction response and calculates the amount of gas used and converts * it to AVAX. Prints results to console. * * @param {TransactionResponse} tx transactionn to extract gas info from * @param {string} methodName Name of method to print */ async function calculateGasFee(tx, methodName) { const gasPrice = 470000000000; const weiPerAvax = Number('1000000000000000000'); const txReceipt = await tx.wait(); const gasUsed = txReceipt.gasUsed.toString() const avax = gasUsed * gasPrice / weiPerAvax; console.log(methodName, "gas used:", gasUsed); console.log(methodName, "AVAX cost:", avax); } /** * Calls transfer on the provided contract. Transfers the ERC20 from the from signer * to the to signer for the amount of amount. * * @param {Signer} from signer to send from * @param {Signer} to signer to send to * @param {number} amount amount to send * @param {Contract} coinContract ERC20 contract to call */ async function sendERC20(from, to, amount, coinContract) { const coin = coinContract.connect(from); tx = await coin.transfer(to.getAddress(), amount); await calculateGasFee(tx, "Transfer"); } // We recommend this pattern to be able to use async/await everywhere // and properly handle errors. main() .then(() => process.exit(0)) .catch(error => { console.error(error); process.exit(1); }); ================================================ FILE: scripts/fund-cchain-addresses.js ================================================ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const avalanche_1 = require('avalanche'); const avm_1 = require('avalanche/dist/apis/avm'); const evm_1 = require('avalanche/dist/apis/evm'); const utils_1 = require('avalanche/dist/utils'); const sleep = (ms) => { return new Promise((resolve) => setTimeout(resolve, ms)); }; const ip = 'localhost'; const port = 9650; const protocol = 'http'; const networkID = 12345; const avalanche = new avalanche_1.Avalanche(ip, port, protocol, networkID); const mstimeout = 3000; const xchain = avalanche.XChain(); const cchain = avalanche.CChain(); const bintools = avalanche_1.BinTools.getInstance(); const xKeychain = xchain.keyChain(); const cKeychain = cchain.keyChain(); const privKeys = [ 'PrivateKey-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN', 'PrivateKey-wHR4zmr9am94KVYnV2aRR4QXt78cuGebt1GpYNwJYEbfAGonj', 'PrivateKey-AR874kuHtHpDk7ntffuEQ9cwiQLL2dz1DmJankW1EyXnz5fc7', 'PrivateKey-Ntk8vV7zaWzAot2wuDXK4e9ZGFUnU49AYTDew5XUyYaNz2u9d', 'PrivateKey-oLM8XbXxXmBHVbdKm2tRYQ1WdMj3b2NggftQpvDUXWSMtdY4i', 'PrivateKey-2kjfDc9RVUQJnu3HQDGiVdxvhM9BmR3UTx7Aq8AJ82G2MspATy', 'PrivateKey-2Rh5Gtu28ca7PS6rLfN6uou9ext8Y5xhoAJDdWPU7GESBLHtv6', 'PrivateKey-2ZcbEPKkXjswsNRBGViGzruReAtTAxW9hsGeMc2GgppnJnDgne', 'PrivateKey-22SYvqaRgFtPJfiZmswrCyE57UcssLVnNPDJ48PYAiCjKVAGy7', 'PrivateKey-tYRsRPijLo6KD2azMLzkcB2ZUndU3a2dJ8kEqBtqesa85pWhB' ]; privKeys.forEach((privKey) => { xKeychain.importKey(privKey); cKeychain.importKey(privKey); }); const xAddresses = xchain.keyChain().getAddresses(); const xAddressStrings = xchain.keyChain().getAddressStrings(); const cAddressStrings = cchain.keyChain().getAddressStrings(); const cAddresses = cchain.keyChain().getAddresses(); const cHexAddresses = [ '0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC', '0x9632a79656af553f58738b0fb750320158495942', '0x55ee05df718f1a5c1441e76190eb1a19ee2c9430', '0x4cf2ed3665f6bfa95ce6a11cfdb7a2ef5fc1c7e4', '0x0b891db1901d4875056896f28b6665083935c7a8', '0x01f253be2ebf0bd64649fa468bf7b95ca933bde2', '0x78A23300E04FB5d5D2820E23cc679738982e1fd5', '0x3c7dae394bbf8e9ee1359ad14c1c47003bd06293', '0x61e0b3cd93f36847abbd5d40d6f00a8ec6f3cffb', '0x0fa8ea536be85f32724d57a37758761b86416123' ]; const cChainBlockchainID = utils_1.Defaults.network['12345'].C.blockchainID; const cChainBlockchainIdBuf = bintools.cb58Decode(cChainBlockchainID); const xChainBlockchainID = utils_1.Defaults.network['12345'].X.blockchainID; const xChainBlockchainIdBuf = bintools.cb58Decode(xChainBlockchainID); const exportedOuts = []; const outputs = []; const inputs = []; const importedIns = []; const evmOutputs = []; const fee = xchain.getDefaultTxFee(); const locktime = new avalanche_1.BN(0); const threshold = 1; const memo = bintools.stringToBuffer( 'AVM utility method buildExportTx to export AVAX to the C-Chain from the X-Chain' ); const waitForUtxo = async () => { const u = await cchain.getUTXOs(cAddressStrings[0], 'X'); if (u.utxos.getAllUTXOs().length) { return u.utxos.getAllUTXOs(); } else { await sleep(mstimeout); return waitForUtxo(); } }; const main = async () => { const avaxAssetID = await xchain.getAVAXAssetID(); const getBalanceResponse = await xchain.getBalance( xAddressStrings[0], bintools.cb58Encode(avaxAssetID) ); const balance = new avalanche_1.BN(getBalanceResponse.balance); const avmUTXOResponse = await xchain.getUTXOs(xAddressStrings); const avmUTXOSet = avmUTXOResponse.utxos; const avmUTXOs = avmUTXOSet.getAllUTXOs(); // 1,000 AVAX const amount = new avalanche_1.BN(1000000000000); console.log('Exporting 1000 AVAX to each address on the C-Chain...'); let secpTransferOutput = new avm_1.SECPTransferOutput( amount.mul(new avalanche_1.BN(10)), [cAddresses[0]], locktime, threshold ); let transferableOutput = new avm_1.TransferableOutput( avaxAssetID, secpTransferOutput ); exportedOuts.push(transferableOutput); secpTransferOutput = new avm_1.SECPTransferOutput( balance.sub(amount.mul(new avalanche_1.BN(10))).sub(fee), xAddresses, locktime, threshold ); transferableOutput = new avm_1.TransferableOutput( avaxAssetID, secpTransferOutput ); outputs.push(transferableOutput); avmUTXOs.forEach((utxo) => { const amountOutput = utxo.getOutput(); const amt = amountOutput.getAmount().clone(); const txid = utxo.getTxID(); const outputidx = utxo.getOutputIdx(); const secpTransferInput = new avm_1.SECPTransferInput(amt); secpTransferInput.addSignatureIdx(0, xAddresses[0]); const input = new avm_1.TransferableInput( txid, outputidx, avaxAssetID, secpTransferInput ); inputs.push(input); }); const exportTx = new avm_1.ExportTx( networkID, bintools.cb58Decode(xChainBlockchainID), outputs, inputs, memo, bintools.cb58Decode(cChainBlockchainID), exportedOuts ); const avmUnsignedTx = new avm_1.UnsignedTx(exportTx); const avmTx = avmUnsignedTx.sign(xKeychain); const avmTXID = await xchain.issueTx(avmTx); console.log(avmTXID); await sleep(mstimeout); console.log('Importing AVAX to the C-Chain...'); console.log('Please wait'); const utxos = await waitForUtxo(); utxos.forEach((utxo, index) => { const assetID = utxo.getAssetID(); const txid = utxo.getTxID(); const outputidx = utxo.getOutputIdx(); const output = utxo.getOutput(); const amt = output.getAmount().clone(); const input = new evm_1.SECPTransferInput(amt); input.addSignatureIdx(0, cAddresses[0]); const xferin = new evm_1.TransferableInput(txid, outputidx, assetID, input); importedIns.push(xferin); cHexAddresses.forEach((cHexAddress) => { const evmOutput = new evm_1.EVMOutput( cHexAddress, new avalanche_1.BN(1000000000), assetID ); evmOutputs.push(evmOutput); }); }); const importTx = new evm_1.ImportTx( networkID, cChainBlockchainIdBuf, xChainBlockchainIdBuf, importedIns, evmOutputs ); const evmUnsignedTx = new evm_1.UnsignedTx(importTx); const evmTx = evmUnsignedTx.sign(cKeychain); const evmTXID = await cchain.issueTx(evmTx); console.log(evmTXID); }; main(); ================================================ FILE: scripts/nft.ts ================================================ import { Contract } from "ethers" import { ethers } from "hardhat" import { abi } from "../artifacts/contracts/NFT.sol/NFT.json" const coinAddr: string = "0xe304EDd5C4e590e2b8ce08b9625597FF38192D71" const main = async (): Promise => { const contract: Contract = new Contract(coinAddr, abi, ethers.provider) const tokenID: number = 395 const tokenURI = await contract.tokenURI(tokenID) console.log(tokenURI) } main() .then(() => process.exit(0)) .catch(error => { console.error(error) process.exit(1) }) ================================================ FILE: scripts/sendAvaxJSONProvider.ts ================================================ import { utils } from "ethers" import { ethers } from "hardhat" interface Param { from: string to: string value: string } const main = async(): Promise => { const from: string = "0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC" const to: string = "0xDd1749831fbF70d88AB7bB07ef7CD9c53D054a57" const amount: string = "0.01" const params: Param[] = [{ from: from, to: to, value: utils.parseUnits(amount, "ether").toHexString() }] const tx: any = await ethers.provider.send("eth_sendTransaction", params) console.log(tx) } main() .then(() => process.exit(0)) .catch(error => { console.error(error) process.exit(1) }) ================================================ FILE: scripts/sendAvaxWalletSigner.ts ================================================ import { BigNumber, providers, utils, Wallet } from "ethers" const walletAddress: string = "0xDd1749831fbF70d88AB7bB07ef7CD9c53D054a57" const pk: string = "0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027" const protocol: string = "http" const host: string = "localhost" const port: number = 9650 const main = async(): Promise => { const provider: providers.JsonRpcProvider = new providers.JsonRpcProvider(`${protocol}://${host}:${port}/ext/bc/C/rpc`) const num: number = await provider.getBlockNumber() console.log(num) const balance: BigNumber = await provider.getBalance(walletAddress) console.log(balance) const formatted: string = utils.formatEther(balance) console.log(formatted) const parsed: BigNumber = utils.parseEther("1.0") console.log(parsed) const wallet: Wallet = new Wallet(pk, provider) const tx: providers.TransactionResponse = await wallet.sendTransaction({ to: walletAddress, value: parsed }); console.log(tx) } main() .then(() => process.exit(0)) .catch(error => { console.error(error) process.exit(1) }) ================================================ FILE: scripts/storage.ts ================================================ import { BigNumber, Contract } from "ethers" import { ethers } from "hardhat" const contractName: string = "Storage" const contractAddress: string = "0xE3573540ab8A1C4c754Fd958Dc1db39BBE81b208" const main = async(): Promise => { const contract: Contract = await ethers.getContractAt(contractName, contractAddress) const num: BigNumber = await contract.retrieve() console.log(`Number: ${num.toString()}`) const tx = await contract.store(507) console.log(tx) } main() ================================================ FILE: setup.js ================================================ const hardhat = require("hardhat") const Web3 = require("web3") ================================================ FILE: test/Coin.js ================================================ // test/Airdrop.js // Load dependencies const { expect } = require('chai'); const { BigNumber } = require('ethers'); const { ethers } = require('hardhat'); const Web3 = require('web3'); const OWNER_ADDRESS = ethers.utils.getAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"); const DECIMALS = 2; const AMT = 150 /////////////////////////////////////////////////////////// // SEE https://hardhat.org/tutorial/testing-contracts.html // FOR HELP WRITING TESTS // USE https://github.com/gnosis/mock-contract FOR HELP // WITH MOCK CONTRACT /////////////////////////////////////////////////////////// // Start test block describe('Coin', function () { before(async function () { this.Coin = await ethers.getContractFactory("ExampleERC20"); this.MockContract = await ethers.getContractFactory("contracts/MockContract.sol:MockContract"); }); beforeEach(async function () { this.coin = await this.Coin.deploy() await this.coin.deployed() this.mock = await this.MockContract.deploy() await this.mock.deployed() }); // Test cases ////////////////////////////// // Constructor ////////////////////////////// describe("Constructor", function () { it('mock test', async function () { // If another contract calls balanceOf on the mock contract, return AMT const balanceOf = Web3.utils.sha3('balanceOf(address)').slice(0,10); await this.mock.givenMethodReturnUint(balanceOf, AMT); }); }); ////////////////////////////// // setRemainderDestination ////////////////////////////// describe("otherMethod", function () { }); }); ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "resolveJsonModule": true } }