Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Staking workflow e2e test #49

Merged
merged 5 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: e2e

on:
pull_request: {}
push:
branches:
- "main"

jobs:
test:
runs-on: ubuntu-latest
env:
INFURA_API_KEY: ${{ secrets.INFURA_API_KEY }}
strategy:
fail-fast: false
matrix:
toml:
- "omnibus-mainnet.toml"
- "omnibus-optimism-mainnet.toml"
- "omnibus-goerli.toml"
# TODO: re-enable the test when we figure out fix for optimism goerli
#- "omnibus-optimism-goerli.toml"

include:
- toml: "omnibus-mainnet.toml"
chainId: 1
providerUrl: "https://mainnet.infura.io/v3/$INFURA_API_KEY"

- toml: "omnibus-optimism-mainnet.toml"
chainId: 10
providerUrl: "https://optimism-mainnet.infura.io/v3/$INFURA_API_KEY"

- toml: "omnibus-goerli.toml"
chainId: 5
providerUrl: "https://goerli.infura.io/v3/$INFURA_API_KEY"

#- toml: "omnibus-optimism-goerli.toml"
# chainId: 420
# providerUrl: "https://optimism-goerli.infura.io/v3/$INFURA_API_KEY"

steps:
- name: Install Foundry (Cannon)
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
- run: anvil -V
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: "18"
cache: "yarn"
- run: yarn --version
- run: yarn install --immutable
- run: yarn build ${{ matrix.chainId }} ${{ matrix.toml }}
- name: "Tests"
run: |
echo "Run anvil"
anvil --fork-url ${{ matrix.providerUrl }} &

echo "Wait for anvil 127.0.0.1:8545"
wget -q -O - --retry-connrefused --waitretry=20 --read-timeout=20 --timeout=15 -t 10 --post-data='{"method":"eth_chainId","params":[],"id":1,"jsonrpc":"2.0"}' --header='Content-Type:application/json' http://127.0.0.1:8545

echo "Execute tests"
yarn test
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
.yarn/versions

node_modules
deployments/
129 changes: 129 additions & 0 deletions e2e/fetch-deployments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#!/usr/bin/env node

const path = require('path');
const fs = require('fs/promises');
const toml = require('@iarna/toml');
const { ethers } = require('ethers');
const { OnChainRegistry, IPFSLoader } = require('@usecannon/builder');

const DEFAULT_REGISTRY_ADDRESS = '0x8E5C7EFC9636A6A0408A46BB7F617094B81e5dba';
const CURRENT_DIR = __dirname;
const ROOT = path.resolve(`${CURRENT_DIR}/..`);

async function fetchDeployment({ chainId, deploymentFile, registry, loader }) {
await fs.mkdir(`${CURRENT_DIR}/deployments/${chainId}`, { recursive: true });

const config = await fs.readFile(deploymentFile, 'utf8');
const { name, version, setting } = toml.parse(config);
const preset = setting?.target_preset?.defaultValue ?? 'main';

const snxAddress = setting?.snx_address?.defaultValue ?? ethers.constants.AddressZero;
await fs.writeFile(
`${CURRENT_DIR}/deployments/${chainId}/snx.json`,
JSON.stringify({ address: snxAddress }, null, 2)
);

const ipfs = await registry.getUrl(`${name}:${version}`, `${chainId}-${preset}`);

const meta = {
name,
version,
preset,
ipfs,
chainId,
deploymentFile: path.relative(ROOT, deploymentFile),
};
await fs.writeFile(
`${CURRENT_DIR}/deployments/${chainId}/meta.json`,
JSON.stringify(meta, null, 2)
);

const deployments = await loader.read(ipfs);

const system = deployments.state['provision.system'].artifacts.imports.system;

// TODO: extract other contracts as necessary
// See https://github.com/Synthetixio/synthetix-v3/blob/main/utils/docgen/abis.js for details

const address = system.contracts.CoreProxy.address;
const abi = new ethers.utils.Interface(system.contracts.CoreProxy.abi).format(
ethers.utils.FormatTypes.full
);
await fs.writeFile(
`${CURRENT_DIR}/deployments/${chainId}/CoreProxy.json`,
JSON.stringify({ address, abi }, null, 2)
);
}

async function run() {
const registry = new OnChainRegistry({
signerOrProvider: `https://mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`,
address: DEFAULT_REGISTRY_ADDRESS,
});
const loader = new IPFSLoader('https://ipfs.synthetix.io');

const [chainId, deploymentFile] = process.argv.slice(2);
if (chainId && deploymentFile) {
await fetchDeployment({ registry, loader, chainId, deploymentFile });
return;
}

await Promise.all([
fetchDeployment({
registry,
loader,
chainId: 1,
deploymentFile: `${ROOT}/omnibus-mainnet.toml`,
}),
fetchDeployment({
registry,
loader,
chainId: 10,
deploymentFile: `${ROOT}/omnibus-optimism-mainnet.toml`,
}),
fetchDeployment({
registry,
loader,
chainId: 5,
deploymentFile: `${ROOT}/omnibus-goerli.toml`,
}),
fetchDeployment({
registry,
loader,
chainId: 420,
deploymentFile: `${ROOT}/omnibus-optimism-goerli.toml`,
}),
fetchDeployment({
registry,
loader,
chainId: 84531,
deploymentFile: `${ROOT}/omnibus-base-goerli.toml`,
}),
fetchDeployment({
registry,
loader,
chainId: 84531,
deploymentFile: `${ROOT}/omnibus-base-goerli-competition.toml`,
}),
fetchDeployment({
registry,
loader,
chainId: 11155111,
deploymentFile: `${ROOT}/omnibus-sepolia.toml`,
}),
fetchDeployment({
registry,
loader,
chainId: 80001,
deploymentFile: `${ROOT}/omnibus-polygon-mumbai.toml`,
}),
fetchDeployment({
registry,
loader,
chainId: 421613,
deploymentFile: `${ROOT}/omnibus-arbitrum-goerli.toml`,
}),
]);
}

run();
26 changes: 26 additions & 0 deletions e2e/tasks/approveCollateral.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const { ethers } = require('ethers');
const { importCoreProxy } = require('./importCoreProxy');
const { getCollateralConfig } = require('./getCollateralConfig');

const log = require('debug')(`tasks:${require('path').basename(__filename, '.js')}`);

async function approveCollateral({ privateKey, symbol }) {
const CoreProxy = await importCoreProxy();
const config = await getCollateralConfig(symbol);
const provider = new ethers.providers.JsonRpcProvider('http://127.0.0.1:8545');
const wallet = new ethers.Wallet(privateKey, provider);
log({ wallet: wallet.address, symbol });

const Token = new ethers.Contract(
config.tokenAddress,
['function approve(address spender, uint256 amount) returns (bool)'],
wallet
);
const tx = await Token.approve(CoreProxy.address, ethers.constants.MaxUint256);
await tx.wait();
return null;
}

module.exports = {
approveCollateral,
};
43 changes: 43 additions & 0 deletions e2e/tasks/borrowUsd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const { ethers } = require('ethers');
const { importCoreProxy } = require('./importCoreProxy');
const { getCollateralConfig } = require('./getCollateralConfig');

const log = require('debug')(`tasks:${require('path').basename(__filename, '.js')}`);

async function borrowUsd({ privateKey, accountId, symbol, amount, poolId }) {
const CoreProxy = await importCoreProxy();
const config = await getCollateralConfig(symbol);
const provider = new ethers.providers.JsonRpcProvider('http://127.0.0.1:8545');
const wallet = new ethers.Wallet(privateKey, provider);
log({ address: wallet.address, accountId, symbol, amount, poolId });

const coreProxy = new ethers.Contract(CoreProxy.address, CoreProxy.abi, wallet);
const position = await coreProxy.getPositionCollateral(
ethers.BigNumber.from(accountId),
ethers.BigNumber.from(poolId),
config.tokenAddress
);
const maxDebt = position.value.div(config.issuanceRatioD18).toNumber();
const debt = Math.floor(maxDebt);

log({
symbol,
issuanceRatio: parseFloat(ethers.utils.formatUnits(config.issuanceRatioD18)),
positionValue: parseFloat(ethers.utils.formatUnits(position.value)),
maxDebt,
debt,
});
const tx = await coreProxy.mintUsd(
ethers.BigNumber.from(accountId),
ethers.BigNumber.from(poolId),
config.tokenAddress,
ethers.utils.parseEther(`${debt}`),
{ gasLimit: 10_000_000 }
);
await tx.wait();
return debt;
}

module.exports = {
borrowUsd,
};
34 changes: 34 additions & 0 deletions e2e/tasks/createAccount.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const { ethers } = require('ethers');
const crypto = require('crypto');
const { importCoreProxy } = require('./importCoreProxy');
const { getAccountOwner } = require('./getAccountOwner');

const log = require('debug')(`tasks:${require('path').basename(__filename, '.js')}`);

async function createAccount({ privateKey, accountId }) {
const CoreProxy = await importCoreProxy();
const provider = new ethers.providers.JsonRpcProvider('http://127.0.0.1:8545');
const wallet = new ethers.Wallet(privateKey, provider);
const coreProxy = new ethers.Contract(CoreProxy.address, CoreProxy.abi, wallet);

// const accountId = parseInt(`1337${crypto.randomInt(1000)}`);
const currentAccountOwner = await getAccountOwner({ accountId });
log({ accountId, currentAccountOwner });

if (currentAccountOwner === wallet.address) {
log({ accountId, result: 'SKIP' });
return accountId;
}

const tx = await coreProxy['createAccount(uint128)'](accountId, { gasLimit: 10_000_000 });
await tx.wait();

const newAccountOwner = await getAccountOwner({ accountId });
log({ accountId, newAccountOwner });

return accountId;
}

module.exports = {
createAccount,
};
31 changes: 31 additions & 0 deletions e2e/tasks/delegateCollateral.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const { ethers } = require('ethers');
const { getCollateralConfig } = require('./getCollateralConfig');
const { importCoreProxy } = require('./importCoreProxy');

const log = require('debug')(`tasks:${require('path').basename(__filename, '.js')}`);

async function delegateCollateral({ privateKey, accountId, symbol, amount, poolId }) {
const CoreProxy = await importCoreProxy();
const config = await getCollateralConfig(symbol);
const provider = new ethers.providers.JsonRpcProvider('http://127.0.0.1:8545');
const wallet = new ethers.Wallet(privateKey, provider);
log({ address: wallet.address, accountId, symbol, amount, poolId });

const coreProxy = new ethers.Contract(CoreProxy.address, CoreProxy.abi, wallet);

const tx = await coreProxy.delegateCollateral(
ethers.BigNumber.from(accountId),
ethers.BigNumber.from(poolId),
config.tokenAddress,
ethers.utils.parseEther(`${amount}`),
ethers.utils.parseEther(`1`),
{ gasLimit: 10_000_000 }
);
await tx.wait();

return accountId;
}

module.exports = {
delegateCollateral,
};
29 changes: 29 additions & 0 deletions e2e/tasks/depositCollateral.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const { ethers } = require('ethers');
const { getCollateralConfig } = require('./getCollateralConfig');
const { importCoreProxy } = require('./importCoreProxy');

const log = require('debug')(`tasks:${require('path').basename(__filename, '.js')}`);

async function depositCollateral({ privateKey, accountId, symbol, amount }) {
const CoreProxy = await importCoreProxy();
const config = await getCollateralConfig(symbol);
const provider = new ethers.providers.JsonRpcProvider('http://127.0.0.1:8545');
const wallet = new ethers.Wallet(privateKey, provider);
log({ address: wallet.address, accountId, symbol, amount });

const coreProxy = new ethers.Contract(CoreProxy.address, CoreProxy.abi, wallet);

const tx = await coreProxy.deposit(
ethers.BigNumber.from(accountId),
config.tokenAddress,
ethers.utils.parseEther(`${amount}`),
{ gasLimit: 10_000_000 }
);
await tx.wait();

return accountId;
}

module.exports = {
depositCollateral,
};
25 changes: 25 additions & 0 deletions e2e/tasks/getAccountCollateral.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const { ethers } = require('ethers');
const { getCollateralConfig } = require('./getCollateralConfig');
const { importCoreProxy } = require('./importCoreProxy');

async function getAccountCollateral({ accountId, symbol }) {
const CoreProxy = await importCoreProxy();
const config = await getCollateralConfig(symbol);
const provider = new ethers.providers.JsonRpcProvider('http://127.0.0.1:8545');
const coreProxy = new ethers.Contract(CoreProxy.address, CoreProxy.abi, provider);

const [totalDeposited, totalAssigned, totalLocked] = await coreProxy.getAccountCollateral(
accountId,
config.tokenAddress
);

return {
totalDeposited: parseFloat(ethers.utils.formatUnits(totalDeposited)),
totalAssigned: parseFloat(ethers.utils.formatUnits(totalAssigned)),
totalLocked: parseFloat(ethers.utils.formatUnits(totalLocked)),
};
}

module.exports = {
getAccountCollateral,
};
Loading