Skip to content

Commit 0cf8d9c

Browse files
feat(deploy): v1 factory contracts on avaxc testnet
Ticket: COIN-1712
1 parent 953f3d2 commit 0cf8d9c

File tree

6 files changed

+294
-7
lines changed

6 files changed

+294
-7
lines changed

.github/workflows/deploy_and_release.yml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ jobs:
1717
- run: npm run test
1818
env:
1919
MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
20+
PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT: ${{ secrets.PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT }}
21+
PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP: ${{ secrets.PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP }}
2022
TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
2123
QUICKNODE_ETH_MAINNET_API_KEY: ${{ secrets.QUICKNODE_ETH_MAINNET_API_KEY }}
2224
QUICKNODE_ETH_HOLESKY_API_KEY: ${{ secrets.QUICKNODE_ETH_HOLESKY_API_KEY }}
@@ -40,13 +42,13 @@ jobs:
4042
result-encoding: string
4143
script: |
4244
const tag = process.env.GITHUB_REF_NAME;
43-
const regex = /v.*\-(eth|hteth|matic|tmatic|bsc|tbsc|arbeth|tarbeth|opeth|topeth)$/;
45+
const regex = /v.*\-(eth|hteth|matic|tmatic|bsc|tbsc|arbeth|tarbeth|opeth|topeth|tavaxc)$/;
4446
const network = tag.match(regex);
4547
return network ? network[1] : "hteth";
4648
deploy-to-test:
4749
runs-on: ubuntu-latest
4850
needs: [lint-and-test, get-network]
49-
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' ) }}
51+
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' ) }}
5052
environment: testnet
5153
steps:
5254
- uses: actions/checkout@v2
@@ -59,6 +61,8 @@ jobs:
5961
- run: npm run deploy-test --network ${{ needs.get-network.outputs.network }}
6062
env:
6163
MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
64+
PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT: ${{ secrets.PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT }}
65+
PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP: ${{ secrets.PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP }}
6266
TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
6367
QUICKNODE_ETH_MAINNET_API_KEY: ${{ secrets.QUICKNODE_ETH_MAINNET_API_KEY }}
6468
QUICKNODE_ETH_HOLESKY_API_KEY: ${{ secrets.QUICKNODE_ETH_HOLESKY_API_KEY }}
@@ -99,7 +103,7 @@ jobs:
99103
deploy-to-prod:
100104
runs-on: ubuntu-latest
101105
needs: [lint-and-test, get-network]
102-
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' ) }}
106+
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' ) }}
103107
environment: mainnet
104108
steps:
105109
- uses: actions/checkout@v2
@@ -112,6 +116,8 @@ jobs:
112116
- run: npm run deploy-prod --network ${{ needs.get-network.outputs.network }}
113117
env:
114118
MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
119+
PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT: ${{ secrets.PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT }}
120+
PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP: ${{ secrets.PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP }}
115121
TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
116122
QUICKNODE_ETH_MAINNET_API_KEY: ${{ secrets.QUICKNODE_ETH_MAINNET_API_KEY }}
117123
QUICKNODE_ETH_HOLESKY_API_KEY: ${{ secrets.QUICKNODE_ETH_HOLESKY_API_KEY }}

.github/workflows/push.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ jobs:
2525
- run: npm run test
2626
env:
2727
MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
28+
PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT: ${{ secrets.PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT }}
29+
PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP: ${{ secrets.PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP }}
2830
TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT: ${{ secrets.TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT }}
2931
QUICKNODE_ETH_MAINNET_API_KEY: ${{ secrets.QUICKNODE_ETH_MAINNET_API_KEY }}
3032
QUICKNODE_ETH_HOLESKY_API_KEY: ${{ secrets.QUICKNODE_ETH_HOLESKY_API_KEY }}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity 0.8.20;
3+
import '../Forwarder.sol';
4+
import '../ERC20Interface.sol';
5+
import '../WalletSimple.sol';
6+
7+
/**
8+
*
9+
* WalletSimple
10+
* ============
11+
*
12+
* Basic multi-signer wallet designed for use in a co-signing environment where 2 signatures are required to move funds.
13+
* Typically used in a 2-of-3 signing configuration. Uses ecrecover to allow for 2 signatures in a single transaction.
14+
*
15+
* The first signature is created on the operation hash (see Data Formats) and passed to sendMultiSig/sendMultiSigToken
16+
* The signer is determined by verifyMultiSig().
17+
*
18+
* The second signature is created by the submitter of the transaction and determined by msg.signer.
19+
*
20+
* Data Formats
21+
* ============
22+
*
23+
* The signature is created with ethereumjs-util.ecsign(operationHash).
24+
* Like the eth_sign RPC call, it packs the values as a 65-byte array of [r, s, v].
25+
* Unlike eth_sign, the message is not prefixed.
26+
*
27+
* The operationHash the result of keccak256(prefix, toAddress, value, data, expireTime).
28+
* For ether transactions, `prefix` is "ETHER".
29+
* For token transaction, `prefix` is "ERC20" and `data` is the tokenContractAddress.
30+
*
31+
*
32+
*/
33+
contract AvaxcWalletSimple is WalletSimple {
34+
/**
35+
* Get the network identifier that signers must sign over
36+
* This provides protection signatures being replayed on other chains
37+
*/
38+
function getNetworkId() internal override pure returns (string memory) {
39+
return 'AVAX';
40+
}
41+
42+
/**
43+
* Get the network identifier that signers must sign over for token transfers
44+
* This provides protection signatures being replayed on other chains
45+
*/
46+
function getTokenNetworkId() internal override pure returns (string memory) {
47+
return 'AVAX-ERC20';
48+
}
49+
50+
/**
51+
* Get the network identifier that signers must sign over for batch transfers
52+
* This provides protection signatures being replayed on other chains
53+
*/
54+
function getBatchNetworkId() internal override pure returns (string memory) {
55+
return 'AVAX-Batch';
56+
}
57+
}

hardhat.config.ts

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import 'hardhat-gas-reporter';
1010
import 'solidity-coverage';
1111

1212
const {
13+
PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT,
14+
PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP,
1315
MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT,
1416
TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT,
1517
QUICKNODE_ETH_MAINNET_API_KEY,
@@ -51,8 +53,11 @@ const config: HardhatUserConfig = {
5153
accounts: [`${MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT}`]
5254
},
5355
hteth: {
54-
url: `https://boldest-cosmological-mountain.ethereum-holesky.quiknode.pro/${QUICKNODE_ETH_HOLESKY_API_KEY}`,
55-
accounts: [`${TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT}`]
56+
url: `https://rpc.holesky.ethpandaops.io/`,
57+
accounts: [
58+
`${PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT}`,
59+
`${PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP}`
60+
]
5661
},
5762
matic: {
5863
url: `https://polygon-mainnet.g.alchemyapi.io/v2/${ALCHEMY_POLYGON_API_KEY}`,
@@ -78,6 +83,14 @@ const config: HardhatUserConfig = {
7883
topeth: {
7984
url: `${QUICKNODE_OPTIMISM_SEPOLIA_API_KEY}`,
8085
accounts: [`${TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT}`]
86+
},
87+
tavaxc: {
88+
url: 'https://api.avax-test.network/ext/C/rpc',
89+
accounts: [`${TESTNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT}`]
90+
},
91+
avaxc: {
92+
url: 'https://api.avax.network/ext/bc/C/rpc',
93+
accounts: [`${MAINNET_PRIVATE_KEY_FOR_CONTRACT_DEPLOYMENT}`]
8194
}
8295
},
8396
gasReporter: {
@@ -100,7 +113,10 @@ const config: HardhatUserConfig = {
100113
arbitrumSepolia: `${ARBISCAN_API_KEY}`,
101114
// optimism
102115
optimisticEthereum: `${OPTIMISTIC_ETHERSCAN_API_KEY}`,
103-
optimisticSepolia: `${OPTIMISTIC_ETHERSCAN_API_KEY}`
116+
optimisticSepolia: `${OPTIMISTIC_ETHERSCAN_API_KEY}`,
117+
// there is free api key for avaxc, so make use of 2 req/sec
118+
axaxc: 'sampleapikey',
119+
avaxcTestnet: 'sampleapikey'
104120
},
105121
customChains: [
106122
{
@@ -126,6 +142,24 @@ const config: HardhatUserConfig = {
126142
apiURL: 'https://api-sepolia-optimistic.etherscan.io/api',
127143
browserURL: 'https://sepolia-optimism.etherscan.io'
128144
}
145+
},
146+
{
147+
network: 'avaxc',
148+
chainId: 43114,
149+
urls: {
150+
apiURL:
151+
'https://api.routescan.io/v2/network/mainnet/evm/43114/etherscan/api',
152+
browserURL: 'https://snowtrace.io/'
153+
}
154+
},
155+
{
156+
network: 'avaxcTestnet',
157+
chainId: 43113,
158+
urls: {
159+
apiURL:
160+
'https://api.routescan.io/v2/network/testnet/evm/43113/etherscan/api',
161+
browserURL: 'https://testnet.snowtrace.io/'
162+
}
129163
}
130164
]
131165
},

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
},
1111
"scripts": {
1212
"deploy-prod": "hardhat run scripts/deploy.ts --network",
13-
"deploy-test": "hardhat run scripts/deploy.ts --network",
13+
"deploy-test": "hardhat run scripts/deployV1FactoryContracts.ts --network",
1414
"test": "hardhat test",
1515
"coverage": "hardhat coverage",
1616
"solhint": "./node_modules/.bin/solhint --fix 'contracts/**/*.sol'",
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import { ethers } from 'hardhat';
2+
import { BigNumber } from 'ethers';
3+
const hre = require('hardhat');
4+
const fs = require('fs');
5+
6+
async function main() {
7+
const output = {
8+
walletImplementation: '',
9+
walletFactory: '',
10+
forwarderImplementation: '',
11+
forwarderFactory: ''
12+
};
13+
14+
const feeData = await ethers.provider.getFeeData();
15+
16+
17+
const [walletDeployer, forwarderDeployer] = await ethers.getSigners();
18+
19+
const gasParams = {
20+
gasPrice: feeData.gasPrice!.mul('2'),
21+
gasLimit: 3000000
22+
};
23+
const walletTxCount = await walletDeployer.getTransactionCount();
24+
25+
console.log('Deploying wallet contracts....');
26+
console.log('Wallet Tx Count: ', walletTxCount);
27+
const walletSelfTransactions = 2 - walletTxCount;
28+
for (let i = 0; i < walletSelfTransactions; i++) {
29+
const tx = await walletDeployer.sendTransaction({
30+
to: walletDeployer.address,
31+
value: ethers.utils.parseEther('0'),
32+
gasPrice: gasParams.gasPrice
33+
});
34+
await tx.wait();
35+
console.log(`Self transaction with nonce: ${i} complete`);
36+
}
37+
38+
const walletImplementationContractName = 'AvaxcWalletSimple';
39+
const walletFactoryContractName = 'WalletFactory';
40+
41+
const WalletImplementation = await ethers.getContractFactory(
42+
walletImplementationContractName,
43+
walletDeployer
44+
);
45+
const walletImplementation = await WalletImplementation.deploy(gasParams);
46+
await walletImplementation.deployed();
47+
output.walletImplementation = walletImplementation.address;
48+
console.log(
49+
`${walletImplementationContractName} deployed at ` +
50+
walletImplementation.address
51+
);
52+
53+
const WalletFactory = await ethers.getContractFactory(
54+
walletFactoryContractName,
55+
walletDeployer
56+
);
57+
const walletFactory = await WalletFactory.deploy(
58+
walletImplementation.address,
59+
gasParams
60+
);
61+
await walletFactory.deployed();
62+
output.walletFactory = walletFactory.address;
63+
console.log(
64+
`${walletFactoryContractName} deployed at ` + walletFactory.address
65+
);
66+
67+
const forwarderTxCount = await forwarderDeployer.getTransactionCount();
68+
69+
console.log('Deploying forwarder contracts....');
70+
console.log('Forwarder Tx Count: ', forwarderTxCount);
71+
const forwarderSelfTransactions = 234 - forwarderTxCount;
72+
73+
for (let i = 0; i < forwarderSelfTransactions; i++) {
74+
const tx = await forwarderDeployer.sendTransaction({
75+
to: forwarderDeployer.address,
76+
value: ethers.utils.parseEther('0'),
77+
gasPrice: gasParams.gasPrice
78+
});
79+
await tx.wait();
80+
console.log(`Self transaction with nonce: ${i} complete`);
81+
}
82+
83+
const forwarderImplementationContractName = 'Forwarder';
84+
const forwarderFactoryContractName = 'ForwarderFactory';
85+
86+
const ForwarderImplementation = await ethers.getContractFactory(
87+
forwarderImplementationContractName,
88+
forwarderDeployer
89+
);
90+
91+
const forwarderImplementation = await ForwarderImplementation.deploy(
92+
gasParams
93+
);
94+
await forwarderImplementation.deployed();
95+
output.forwarderImplementation = forwarderImplementation.address;
96+
97+
console.log(
98+
`${forwarderImplementationContractName} deployed at ` +
99+
forwarderImplementation.address
100+
);
101+
102+
const ForwarderFactory = await ethers.getContractFactory(
103+
forwarderFactoryContractName,
104+
forwarderDeployer
105+
);
106+
107+
const forwarderFactory = await ForwarderFactory.deploy(
108+
forwarderImplementation.address,
109+
gasParams
110+
);
111+
112+
await forwarderFactory.deployed();
113+
output.forwarderFactory = forwarderFactory.address;
114+
console.log(
115+
`${forwarderFactoryContractName} deployed at ` + forwarderFactory.address
116+
);
117+
118+
fs.writeFileSync('output.json', JSON.stringify(output));
119+
120+
// Wait 5 minutes. It takes some time for the etherscan backend to index the transaction and store the contract.
121+
console.log('Waiting for 5 minutes before verifying....');
122+
await new Promise((r) => setTimeout(r, 1000 * 300));
123+
124+
// We have to wait for a minimum of 10 block confirmations before we can call the etherscan api to verify
125+
126+
await walletImplementation.deployTransaction.wait(10);
127+
await walletFactory.deployTransaction.wait(10);
128+
await forwarderImplementation.deployTransaction.wait(10);
129+
await forwarderFactory.deployTransaction.wait(10);
130+
131+
console.log('Done waiting, verifying');
132+
await verifyContract(
133+
walletImplementationContractName,
134+
walletImplementation.address,
135+
[]
136+
);
137+
await verifyContract('WalletFactory', walletFactory.address, [
138+
walletImplementation.address
139+
]);
140+
141+
await verifyContract(
142+
forwarderImplementationContractName,
143+
forwarderImplementation.address,
144+
[]
145+
);
146+
147+
await verifyContract('ForwarderFactory', forwarderFactory.address, [
148+
forwarderImplementation.address
149+
]);
150+
151+
console.log('Contracts verified');
152+
}
153+
154+
async function verifyContract(
155+
contractName: string,
156+
contractAddress: string,
157+
constructorArguments: string[],
158+
contract?: string
159+
) {
160+
try {
161+
const verifyContractArgs: {
162+
address: string;
163+
constructorArguments: string[];
164+
contract?: string;
165+
} = {
166+
address: contractAddress,
167+
constructorArguments: constructorArguments
168+
};
169+
170+
if (contract) {
171+
verifyContractArgs.contract = contract;
172+
}
173+
174+
await hre.run('verify:verify', verifyContractArgs);
175+
} catch (e) {
176+
// @ts-ignore
177+
// We get a failure API response if the source code has already been uploaded, don't throw in this case.
178+
if (!e.message.includes('Reason: Already Verified')) {
179+
throw e;
180+
}
181+
}
182+
console.log(`Verified ${contractName} on Etherscan!`);
183+
}
184+
185+
main().catch((error) => {
186+
console.error(error);
187+
process.exitCode = 1;
188+
});

0 commit comments

Comments
 (0)