Skip to content

Commit

Permalink
feat(deploy): v1 factory contracts on avaxc testnet
Browse files Browse the repository at this point in the history
Ticket: COIN-1712
  • Loading branch information
mullapudipruthvik committed Oct 3, 2024
1 parent 953f3d2 commit e348070
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 8 deletions.
12 changes: 9 additions & 3 deletions .github/workflows/deploy_and_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ jobs:
- run: npm run test
env:
MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT: ${{ secrets.PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT }}
PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP: ${{ secrets.PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP }}
TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
QUICKNODE_ETH_MAINNET_API_KEY: ${{ secrets.QUICKNODE_ETH_MAINNET_API_KEY }}
QUICKNODE_ETH_HOLESKY_API_KEY: ${{ secrets.QUICKNODE_ETH_HOLESKY_API_KEY }}
Expand All @@ -40,13 +42,13 @@ jobs:
result-encoding: string
script: |
const tag = process.env.GITHUB_REF_NAME;
const regex = /v.*\-(eth|hteth|matic|tmatic|bsc|tbsc|arbeth|tarbeth|opeth|topeth)$/;
const regex = /v.*\-(eth|hteth|matic|tmatic|bsc|tbsc|arbeth|tarbeth|opeth|topeth|tavaxc)$/;
const network = tag.match(regex);
return network ? network[1] : "hteth";
deploy-to-test:
runs-on: ubuntu-latest
needs: [lint-and-test, get-network]
if: ${{ (needs.get-network.outputs.network == 'hteth' ) || (needs.get-network.outputs.network == 'tmatic' ) || (needs.get-network.outputs.network == 'tbsc' ) || (needs.get-network.outputs.network == 'tarbeth' ) || (needs.get-network.outputs.network == 'topeth' ) }}
if: ${{ (needs.get-network.outputs.network == 'hteth' ) || (needs.get-network.outputs.network == 'tmatic' ) || (needs.get-network.outputs.network == 'tbsc' ) || (needs.get-network.outputs.network == 'tarbeth' ) || (needs.get-network.outputs.network == 'topeth' ) || (needs.get-network.outputs.network == 'tavaxc' ) }}
environment: testnet
steps:
- uses: actions/checkout@v2
Expand All @@ -59,6 +61,8 @@ jobs:
- run: npm run deploy-test --network ${{ needs.get-network.outputs.network }}
env:
MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT: ${{ secrets.PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT }}
PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP: ${{ secrets.PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP }}
TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
QUICKNODE_ETH_MAINNET_API_KEY: ${{ secrets.QUICKNODE_ETH_MAINNET_API_KEY }}
QUICKNODE_ETH_HOLESKY_API_KEY: ${{ secrets.QUICKNODE_ETH_HOLESKY_API_KEY }}
Expand Down Expand Up @@ -99,7 +103,7 @@ jobs:
deploy-to-prod:
runs-on: ubuntu-latest
needs: [lint-and-test, get-network]
if: ${{ (needs.get-network.outputs.network == 'eth' ) || (needs.get-network.outputs.network == 'matic' ) || (needs.get-network.outputs.network == 'bsc' ) || (needs.get-network.outputs.network == 'arbeth' ) || (needs.get-network.outputs.network == 'opeth' ) }}
if: ${{ (needs.get-network.outputs.network == 'eth' ) || (needs.get-network.outputs.network == 'matic' ) || (needs.get-network.outputs.network == 'bsc' ) || (needs.get-network.outputs.network == 'arbeth' ) || (needs.get-network.outputs.network == 'opeth' ) || (needs.get-network.outputs.network == 'avaxc' ) }}
environment: mainnet
steps:
- uses: actions/checkout@v2
Expand All @@ -112,6 +116,8 @@ jobs:
- run: npm run deploy-prod --network ${{ needs.get-network.outputs.network }}
env:
MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT: ${{ secrets.PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT }}
PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP: ${{ secrets.PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP }}
TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
QUICKNODE_ETH_MAINNET_API_KEY: ${{ secrets.QUICKNODE_ETH_MAINNET_API_KEY }}
QUICKNODE_ETH_HOLESKY_API_KEY: ${{ secrets.QUICKNODE_ETH_HOLESKY_API_KEY }}
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ jobs:
- run: npm run test
env:
MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT: ${{ secrets.PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT }}
PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP: ${{ secrets.PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP }}
TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
QUICKNODE_ETH_MAINNET_API_KEY: ${{ secrets.QUICKNODE_ETH_MAINNET_API_KEY }}
QUICKNODE_ETH_HOLESKY_API_KEY: ${{ secrets.QUICKNODE_ETH_HOLESKY_API_KEY }}
Expand Down
57 changes: 57 additions & 0 deletions contracts/coins/AvaxcWalletSimple.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
import '../Forwarder.sol';
import '../ERC20Interface.sol';
import '../WalletSimple.sol';

/**
*
* WalletSimple
* ============
*
* Basic multi-signer wallet designed for use in a co-signing environment where 2 signatures are required to move funds.
* Typically used in a 2-of-3 signing configuration. Uses ecrecover to allow for 2 signatures in a single transaction.
*
* The first signature is created on the operation hash (see Data Formats) and passed to sendMultiSig/sendMultiSigToken
* The signer is determined by verifyMultiSig().
*
* The second signature is created by the submitter of the transaction and determined by msg.signer.
*
* Data Formats
* ============
*
* The signature is created with ethereumjs-util.ecsign(operationHash).
* Like the eth_sign RPC call, it packs the values as a 65-byte array of [r, s, v].
* Unlike eth_sign, the message is not prefixed.
*
* The operationHash the result of keccak256(prefix, toAddress, value, data, expireTime).
* For ether transactions, `prefix` is "ETHER".
* For token transaction, `prefix` is "ERC20" and `data` is the tokenContractAddress.
*
*
*/
contract AvaxcWalletSimple is WalletSimple {
/**
* Get the network identifier that signers must sign over
* This provides protection signatures being replayed on other chains
*/
function getNetworkId() internal override pure returns (string memory) {
return 'AVAX';
}

/**
* Get the network identifier that signers must sign over for token transfers
* This provides protection signatures being replayed on other chains
*/
function getTokenNetworkId() internal override pure returns (string memory) {
return 'AVAX-ERC20';
}

/**
* Get the network identifier that signers must sign over for batch transfers
* This provides protection signatures being replayed on other chains
*/
function getBatchNetworkId() internal override pure returns (string memory) {
return 'AVAX-Batch';
}
}
46 changes: 43 additions & 3 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import 'hardhat-gas-reporter';
import 'solidity-coverage';

const {
PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT,
PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP,
MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT,
TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT,
QUICKNODE_ETH_MAINNET_API_KEY,
Expand Down Expand Up @@ -51,8 +53,11 @@ const config: HardhatUserConfig = {
accounts: [`${MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT}`]
},
hteth: {
url: `https://boldest-cosmological-mountain.ethereum-holesky.quiknode.pro/${QUICKNODE_ETH_HOLESKY_API_KEY}`,
accounts: [`${TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT}`]
url: `https://rpc.holesky.ethpandaops.io/`,
accounts: [
`${PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT}`,
`${PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP}`
]
},
matic: {
url: `https://polygon-mainnet.g.alchemyapi.io/v2/${ALCHEMY_POLYGON_API_KEY}`,
Expand All @@ -78,6 +83,20 @@ const config: HardhatUserConfig = {
topeth: {
url: `${QUICKNODE_OPTIMISM_SEPOLIA_API_KEY}`,
accounts: [`${TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT}`]
},
tavaxc: {
url: 'https://api.avax-test.network/ext/C/rpc',
accounts: [
`${PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT}`,
`${PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP}`
]
},
avaxc: {
url: 'https://api.avax.network/ext/bc/C/rpc',
accounts: [
`${PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT}`,
`${PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP}`
]
}
},
gasReporter: {
Expand All @@ -100,7 +119,10 @@ const config: HardhatUserConfig = {
arbitrumSepolia: `${ARBISCAN_API_KEY}`,
// optimism
optimisticEthereum: `${OPTIMISTIC_ETHERSCAN_API_KEY}`,
optimisticSepolia: `${OPTIMISTIC_ETHERSCAN_API_KEY}`
optimisticSepolia: `${OPTIMISTIC_ETHERSCAN_API_KEY}`,
// there is free api key for avaxc, so make use of 2 req/sec
avaxc: 'sampleapikey',
avaxcTestnet: 'sampleapikey'
},
customChains: [
{
Expand All @@ -126,6 +148,24 @@ const config: HardhatUserConfig = {
apiURL: 'https://api-sepolia-optimistic.etherscan.io/api',
browserURL: 'https://sepolia-optimism.etherscan.io'
}
},
{
network: 'avaxc',
chainId: 43114,
urls: {
apiURL:
'https://api.routescan.io/v2/network/mainnet/evm/43114/etherscan/api',
browserURL: 'https://snowtrace.io/'
}
},
{
network: 'avaxcTestnet',
chainId: 43113,
urls: {
apiURL:
'https://api.routescan.io/v2/network/testnet/evm/43113/etherscan/api',
browserURL: 'https://testnet.snowtrace.io/'
}
}
]
},
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"test": "test"
},
"scripts": {
"deploy-prod": "hardhat run scripts/deploy.ts --network",
"deploy-test": "hardhat run scripts/deploy.ts --network",
"deploy-prod": "hardhat run scripts/deployV1FactoryContracts.ts --network",
"deploy-test": "hardhat run scripts/deployV1FactoryContracts.ts --network",
"test": "hardhat test",
"coverage": "hardhat coverage",
"solhint": "./node_modules/.bin/solhint --fix 'contracts/**/*.sol'",
Expand Down
187 changes: 187 additions & 0 deletions scripts/deployV1FactoryContracts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import { ethers } from 'hardhat';
import { BigNumber } from 'ethers';
const hre = require('hardhat');
const fs = require('fs');

async function main() {
const output = {
walletImplementation: '',
walletFactory: '',
forwarderImplementation: '',
forwarderFactory: ''
};

const feeData = await ethers.provider.getFeeData();

const [walletDeployer, forwarderDeployer] = await ethers.getSigners();

const gasParams = {
gasPrice: feeData.gasPrice!.mul('2'),
gasLimit: 5000000
};
const walletTxCount = await walletDeployer.getTransactionCount();

console.log('Deploying wallet contracts....');
console.log('Wallet Tx Count: ', walletTxCount);
const walletSelfTransactions = 2 - walletTxCount;
for (let i = 0; i < walletSelfTransactions; i++) {
const tx = await walletDeployer.sendTransaction({
to: walletDeployer.address,
value: ethers.utils.parseEther('0'),
gasPrice: gasParams.gasPrice
});
await tx.wait();
console.log(`Self transaction with nonce: ${i} complete`);
}

const walletImplementationContractName = 'AvaxcWalletSimple';
const walletFactoryContractName = 'WalletFactory';

const WalletImplementation = await ethers.getContractFactory(
walletImplementationContractName,
walletDeployer
);
const walletImplementation = await WalletImplementation.deploy(gasParams);
await walletImplementation.deployed();
output.walletImplementation = walletImplementation.address;
console.log(
`${walletImplementationContractName} deployed at ` +
walletImplementation.address
);

const WalletFactory = await ethers.getContractFactory(
walletFactoryContractName,
walletDeployer
);
const walletFactory = await WalletFactory.deploy(
walletImplementation.address,
gasParams
);
await walletFactory.deployed();
output.walletFactory = walletFactory.address;
console.log(
`${walletFactoryContractName} deployed at ` + walletFactory.address
);

const forwarderTxCount = await forwarderDeployer.getTransactionCount();

console.log('Deploying forwarder contracts....');
console.log('Forwarder Tx Count: ', forwarderTxCount);
const forwarderSelfTransactions = 234 - forwarderTxCount;

for (let i = 0; i < forwarderSelfTransactions; i++) {
const tx = await forwarderDeployer.sendTransaction({
to: forwarderDeployer.address,
value: ethers.utils.parseEther('0'),
gasPrice: gasParams.gasPrice
});
await tx.wait();
console.log(`Self transaction with nonce: ${i} complete`);
}

const forwarderImplementationContractName = 'Forwarder';
const forwarderFactoryContractName = 'ForwarderFactory';

const ForwarderImplementation = await ethers.getContractFactory(
forwarderImplementationContractName,
forwarderDeployer
);

const forwarderImplementation = await ForwarderImplementation.deploy(
gasParams
);
await forwarderImplementation.deployed();
output.forwarderImplementation = forwarderImplementation.address;

console.log(
`${forwarderImplementationContractName} deployed at ` +
forwarderImplementation.address
);

const ForwarderFactory = await ethers.getContractFactory(
forwarderFactoryContractName,
forwarderDeployer
);

const forwarderFactory = await ForwarderFactory.deploy(
forwarderImplementation.address,
gasParams
);

await forwarderFactory.deployed();
output.forwarderFactory = forwarderFactory.address;
console.log(
`${forwarderFactoryContractName} deployed at ` + forwarderFactory.address
);

fs.writeFileSync('output.json', JSON.stringify(output));

// Wait 5 minutes. It takes some time for the etherscan backend to index the transaction and store the contract.
console.log('Waiting for 5 minutes before verifying....');
await new Promise((r) => setTimeout(r, 1000 * 300));

// We have to wait for a minimum of 10 block confirmations before we can call the etherscan api to verify

await walletImplementation.deployTransaction.wait(10);
await walletFactory.deployTransaction.wait(10);
await forwarderImplementation.deployTransaction.wait(10);
await forwarderFactory.deployTransaction.wait(10);

console.log('Done waiting, verifying');
await verifyContract(
walletImplementationContractName,
walletImplementation.address,
[]
);
await verifyContract('WalletFactory', walletFactory.address, [
walletImplementation.address
]);

await verifyContract(
forwarderImplementationContractName,
forwarderImplementation.address,
[]
);

await verifyContract('ForwarderFactory', forwarderFactory.address, [
forwarderImplementation.address
]);

console.log('Contracts verified');
}

async function verifyContract(
contractName: string,
contractAddress: string,
constructorArguments: string[],
contract?: string
) {
try {
const verifyContractArgs: {
address: string;
constructorArguments: string[];
contract?: string;
} = {
address: contractAddress,
constructorArguments: constructorArguments
};

if (contract) {
verifyContractArgs.contract = contract;
}

await hre.run('verify:verify', verifyContractArgs);
} catch (e) {
// @ts-ignore
// We get a failure API response if the source code has already been uploaded, don't throw in this case.
if (!e.message.includes('Reason: Already Verified')) {
throw e;
}
}
console.log(`Verified ${contractName} on Etherscan!`);
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});

0 comments on commit e348070

Please sign in to comment.