Skip to content

Commit 23d27d9

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

File tree

6 files changed

+300
-8
lines changed

6 files changed

+300
-8
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: 43 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,20 @@ 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: [
90+
`${PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT}`,
91+
`${PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP}`
92+
]
93+
},
94+
avaxc: {
95+
url: 'https://api.avax.network/ext/bc/C/rpc',
96+
accounts: [
97+
`${PRIVATE_KEY_FOR_V1_WALLET_CONTRACT_DEPLOYMENT}`,
98+
`${PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT_BACKUP}`
99+
]
81100
}
82101
},
83102
gasReporter: {
@@ -100,7 +119,10 @@ const config: HardhatUserConfig = {
100119
arbitrumSepolia: `${ARBISCAN_API_KEY}`,
101120
// optimism
102121
optimisticEthereum: `${OPTIMISTIC_ETHERSCAN_API_KEY}`,
103-
optimisticSepolia: `${OPTIMISTIC_ETHERSCAN_API_KEY}`
122+
optimisticSepolia: `${OPTIMISTIC_ETHERSCAN_API_KEY}`,
123+
// there is free api key for avaxc, so make use of 2 req/sec
124+
axaxc: 'sampleapikey',
125+
avaxcTestnet: 'sampleapikey'
104126
},
105127
customChains: [
106128
{
@@ -126,6 +148,24 @@ const config: HardhatUserConfig = {
126148
apiURL: 'https://api-sepolia-optimistic.etherscan.io/api',
127149
browserURL: 'https://sepolia-optimism.etherscan.io'
128150
}
151+
},
152+
{
153+
network: 'avaxc',
154+
chainId: 43114,
155+
urls: {
156+
apiURL:
157+
'https://api.routescan.io/v2/network/mainnet/evm/43114/etherscan/api',
158+
browserURL: 'https://snowtrace.io/'
159+
}
160+
},
161+
{
162+
network: 'avaxcTestnet',
163+
chainId: 43113,
164+
urls: {
165+
apiURL:
166+
'https://api.routescan.io/v2/network/testnet/evm/43113/etherscan/api',
167+
browserURL: 'https://testnet.snowtrace.io/'
168+
}
129169
}
130170
]
131171
},

package.json

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

0 commit comments

Comments
 (0)