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

Connext integration #3

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@
"dependencies": {
"@apollo/client": "^3.3.20",
"@arcxmoney/analytics": "^1.6.0",
"@connext/chain-abstraction": "1.0.2-alpha.11",
"@connext/sdk": "^2.0.2",
"@ethersproject/abi": "^5.7.0",
"@ethersproject/constants": "^5.7.0",
"@ethersproject/contracts": "^5.7.0",
Expand Down Expand Up @@ -128,13 +130,16 @@
"assert": "^2.0.0",
"axios": "^0.21.1",
"axios-cache-adapter": "^2.7.3",
"browserify-zlib": "^0.2.0",
"canvas-confetti": "^1.6.0",
"crypto-browserify": "^3.12.0",
"cryptocurrency-icons": "^0.17.2",
"decimal.js-light": "^2.5.1",
"dotenv": "^16.0.3",
"eth-provider": "^0.12.1",
"ethereumjs-util": "^7.1.3",
"ethers": "^5",
"fs": "^0.0.1-security",
"graphql": "^15.5.0",
"graphql-tag": "^2.12.4",
"https-browserify": "^1.0.0",
Expand All @@ -145,6 +150,7 @@
"md5": "^2.3.0",
"notistack": "^2.0.4",
"os-browserify": "^0.3.0",
"path": "^0.12.7",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-intl": "^6.3.2",
Expand All @@ -163,7 +169,9 @@
"url": "^0.11.0",
"uuid": "^9.0.0",
"wagmi": "^0.12.7",
"web3modal": "^1.9.3"
"web3modal": "^1.9.3",
"zlib": "^1.0.5",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are all of these new dependencies needed?

"zlib-browserify": "^0.0.3"
},
"lint-staged": {
"*.{js,jsx,ts,tsx,css,md}": [
Expand Down
62 changes: 62 additions & 0 deletions src/common/utils/connext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { ethers } from 'ethers';
import { PROTOCOL_TOKEN_ADDRESS } from '@common/mocks/tokens';
import { DestinationCallDataParams } from '@connext/chain-abstraction/dist/types';

interface Permissions {
operator: string;
permissions: number[];
}

export const getSwapAndXcallParams = (
originDomain: string,
destinationDomain: string,
fromAsset: string,
toAsset: string,
amountIn: string,
to: string, // mean target address.
callData: string,
relayerFeeInNativeAsset: string
) => {
const fromAssetOrigin = fromAsset === PROTOCOL_TOKEN_ADDRESS ? ethers.constants.AddressZero : fromAsset;
const swapAndXCallParams = {
originDomain,
destinationDomain,
fromAsset: fromAssetOrigin, // BNB
toAsset, // USDC
amountIn: amountIn.toString(),
to,
relayerFeeInNativeAsset, // 0.001 BNB
callData,
};
return swapAndXCallParams;
};

export const getForwardFunctionCallHelper = (
from: string,
to: string,
amountOfSwaps: ethers.BigNumber,
swapInterval: ethers.BigNumber,
owner: string,
permissions: Permissions[]
) => {
const { defaultAbiCoder } = ethers.utils;
const encodedData = defaultAbiCoder.encode(
['address', 'address', 'uint256', 'uint32', 'address', 'tuple(address operator,uint256[] permissions)[]'],
[from, to, amountOfSwaps, swapInterval, owner, permissions]
);
return encodedData;
};

export const getDestinationCallDataParams = (signerAddress: string, token: string, poolFee: string) => {
const params: DestinationCallDataParams = {
fallback: signerAddress,
swapForwarderData: {
toAsset: token,
swapData: {
amountOutMin: '0',
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be 0 or should the value change?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine

poolFee,
},
},
};
return params;
};
68 changes: 68 additions & 0 deletions src/constants/addresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -718,4 +718,72 @@ export const getGhTokenListLogoUrl = (chainId: number, address: string) =>
// oasis: 42262,

export const UNSUPPORTED_WAGMI_CHAIN = [122, 128, 106, 42262];

export const SUPPORTED_CHAINS_BY_CONNEXT: Record<number, { domainId: string; name: string; network: string }> = {
1: {
domainId: '6648936',
// Ethereum Mainnet
name: 'Ethereum',
network: 'mainnet',
},
10: {
domainId: '1869640809',
// Optimism
name: 'Optimism',
network: 'mainnet',
},
100: {
domainId: '6778479',
// Gnosis Chain
name: 'Gnosis',
network: 'mainnet',
},
137: {
domainId: '1886350457',
// Polygon
name: 'Polygon',
network: 'mainnet',
},
420: {
domainId: '1735356532',
// Optimism-Goerli
name: 'OptGoerli',
network: 'testnet',
},
42161: {
domainId: '1634886255',
// Arbitrum One
name: 'Arbitrum',
network: 'mainnet',
},
421613: {
domainId: '1734439522',
// Arbitrum-Goerli
name: 'ArbGoerli',
network: 'testnet',
},
5: {
domainId: '1735353714',
// Goerli
name: 'Goerli',
network: 'testnet',
},
56: {
domainId: '6450786',
// BNB Chain
name: 'BNB',
network: 'mainnet',
},
80001: {
domainId: '9991',
// Mumbai
name: 'Mumbai',
network: 'testnet',
},
};

export const X_TARGET_ADDRESS: Record<number, string> = {
137: '0xC825d25123Ca8d49066cf8F0AeE164660056e172',
};

/* eslint-enable */
120 changes: 120 additions & 0 deletions src/services/connextService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { getPoolFeeForUniV3, getXCallCallData, prepareSwapAndXCall } from '@connext/chain-abstraction';
import { DestinationCallDataParams, Swapper, SwapAndXCallParams } from '@connext/chain-abstraction/dist/types';
import { SdkConfig, create } from '@connext/sdk';
import { NETWORKS, SUPPORTED_CHAINS_BY_CONNEXT } from '@constants';
import { find } from 'lodash';
import WalletService from './walletService';

interface DomainID {
[key: number]: string;
}

export default class ConnextService {
walletService: WalletService;

sdkConfig: SdkConfig;

constructor(walletService: WalletService) {
this.walletService = walletService;
}

sdkInit() {
const domainConfig: { [domainId: string]: { providers: string[] } } = {};

const domainChainIds = Object.entries(SUPPORTED_CHAINS_BY_CONNEXT)
.filter(([key]) => typeof key === 'number')
.map(([key, value]) => ({ domainId: value.domainId, chainId: key }));

domainChainIds.forEach((obj) => {
domainConfig[obj.domainId] = { providers: [this.getRPCURL(parseInt(obj.chainId, 10))] };
});

const sdkConfig: SdkConfig = {
signerAddress: this.walletService.getAccount(),
network: 'mainnet', // can change it to testnet as well
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this parameter for?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For initing the connext core SDK

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right but I mean, what does it imply if it is "mainnet"? or "testnet"? shouldn't it work the same regardless of what type of network it is?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So for initialising the SDK we have to define if this is for mainnet or testnet. If we define any env for the environment we can modify it according to that!

Comment on lines +22 to +34
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I woudl move this all into its own function, since walletService.getAccount() will return the current account and at the moment of the constructor the wallet is not yet connected, so its always going to be empty.

The idea would be that on the setAccount method of the web3Service.ts we call that function to regenerate the config with the new address

chains: domainConfig,
};
this.sdkConfig = sdkConfig;
return sdkConfig;
}

getRPCURL(chainID: number) {
const network = find(NETWORKS, { chainId: chainID });
if (network) {
return network.rpc[0];
}
throw Error('Network not supported');
}

async getCalculatedRelayerFees(originDomain: string, destinationDomain: string) {
const { sdkBase } = await create(this.sdkConfig);
const relayerFees = await sdkBase.estimateRelayerFee({
originDomain,
destinationDomain,
isHighPriority: true,
});
return relayerFees.toString();
}

async getPoolFeeForUniV3Helper(domainID: string, token0: string, token1: string, rpcURL: string) {
try {
const poolFee = await getPoolFeeForUniV3(domainID, rpcURL, token0, token1);
return poolFee;
} catch (err) {
throw Error('Failed to fetch Pool Fees');
}
}

async getXCallCallDataHelper(domainID: string, forwardCallData: string, params: DestinationCallDataParams) {
const swapper = Swapper.UniV3;
return getXCallCallData(domainID, swapper, forwardCallData, params);
}

async prepareSwapAndXCallHelper(swapAndXCallParams: SwapAndXCallParams, signerAddress: string) {
return prepareSwapAndXCall(swapAndXCallParams, signerAddress);
}

async getEstimateAmountReceived(
originDomain: string,
destinationDomain: string,
originToken: string,
amount: number
) {
const { sdkBase } = await create(this.sdkConfig);
const estimateReceived = await sdkBase.calculateAmountReceived(
originDomain,
destinationDomain,
originToken,
amount
);
return estimateReceived;
}

getNativeUSDCAddress(networkName: number) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will connext only work with USDC?

const USDC_ADDRESS: DomainID = {
1: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
137: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
10: '0x7F5c764cBc14f9669B88837ca1490cCa17c31607',
42161: '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8',
100: '0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83',
56: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d',
};
return USDC_ADDRESS[networkName];
}

async getTransferStatus(transactionHash: string) {
try {
const { sdkUtils } = await create(this.sdkConfig);
const params: { transactionHash: string } = {
transactionHash,
};
const transferStatus = await sdkUtils.getTransfers(params);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how can it know in which chain is it bridging if it only has the transactionHash?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it is the internal function we use for connextscan, it can fetch the whole transaction from origin transactionHash itself, it will give you chains and other details as well

if (!transferStatus) {
throw Error('Failed to fetch transfer status');
}
return transferStatus;
} catch (err) {
throw Error(err);
}
}
}
Loading