Skip to content
This repository has been archived by the owner on May 16, 2024. It is now read-only.

CORE-1932 update deposit flow #387

Closed
wants to merge 6 commits into from
Closed
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
17 changes: 17 additions & 0 deletions src/ImmutableX.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Config } from './config';
import { ImmutableX } from './ImmutableX';

describe('ImmutableXClient', () => {
it('GET starkex contract version', async () => {
const client = new ImmutableX(Config.SANDBOX);
const contractVersion = await client.getStarkExContractVersion();

expect(contractVersion).toHaveProperty('version');
expect(contractVersion).toHaveProperty('message');
expect(contractVersion.message).not.toBeUndefined();
expect(contractVersion.message).not.toBeNull();
expect(contractVersion.version).not.toBeUndefined();
expect(contractVersion.version).not.toBeNull();
expect(contractVersion.version).not.toEqual('');
});
});
9 changes: 9 additions & 0 deletions src/ImmutableX.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,15 @@ export class ImmutableX {
});
}

public getStarkExContractVersion() {
return this.workflows
.getStarkExContractVersion()
.then(res => res.data)
.catch(err => {
throw formatError(err);
});
}

/**
* Create a Collection
* @param ethSigner - the L1 signer
Expand Down
5 changes: 5 additions & 0 deletions src/types/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,8 @@ export type UnsignedExchangeTransferRequest = ExchangeTokenAmount & {
*/
transactionID: string;
};

export interface StarkExContractVersion {
version: string;
message: string;
}
37 changes: 37 additions & 0 deletions src/workflows.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Config } from './config';

import axios from 'axios';
import { Workflows } from './workflows/workflows';

jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;

describe('getContractVersion', () => {
afterEach(() => {
jest.clearAllMocks();
});

it('should return 3 when contract is 3.0.3', async () => {
const workflows = new Workflows(Config.SANDBOX);
mockedAxios.get.mockResolvedValueOnce({
data: {
version: '3.0.3',
message: '',
},
});

const contractVersion = await workflows.getStarkExContractVersion();
expect(contractVersion.data.version).toEqual('3.0.3');
});

it('should return 0 on 404 error', async () => {
const workflows = new Workflows(Config.SANDBOX);
mockedAxios.get.mockRejectedValueOnce(
new Error('Request failed with status code 404'),
);

await expect(workflows.getStarkExContractVersion()).rejects.toThrowError(
new Error('Request failed with status code 404'),
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ async function executeDepositERC721(
return signer.sendTransaction(populatedTransaction);
}

export async function depositERC721Workflow(
export async function depositERC721WorkflowV3(
signer: Signer,
deposit: ERC721Token,
depositsApi: DepositsApi,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ async function executeDepositEth(
return signer.sendTransaction({ ...populatedTransaction, value: amount });
}

export async function depositEthWorkflow(
export async function depositEthWorkflowV3(
signer: Signer,
deposit: ETHAmount,
depositsApi: DepositsApi,
Expand Down
106 changes: 106 additions & 0 deletions src/workflows/depositV4/depositERC20.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { Signer } from '@ethersproject/abstract-signer';
import { TransactionResponse } from '@ethersproject/providers';
import { DepositsApi, EncodingApi, TokensApi } from '../../api';
import { parseUnits } from '@ethersproject/units';
import { Core, Core__factory, IERC20__factory } from '../../contracts';
import { ERC20Amount } from '../../types';
import { BigNumber } from '@ethersproject/bignumber';
import { ImmutableXConfiguration } from '../../config';

interface ERC20TokenData {
decimals: number;
token_address: string;
}

async function executeDepositERC20(
signer: Signer,
quantizedAmount: BigNumber,
assetType: string,
starkPublicKey: string,
vaultId: number,
contract: Core,
): Promise<TransactionResponse> {
const populatedTransaction = await contract.populateTransaction.depositERC20(
starkPublicKey,
assetType,
vaultId,
quantizedAmount,
);

return signer.sendTransaction(populatedTransaction);
}

export async function depositERC20WorkflowV4(
signer: Signer,
deposit: ERC20Amount,
depositsApi: DepositsApi,
tokensApi: TokensApi,
encodingApi: EncodingApi,
config: ImmutableXConfiguration,
): Promise<TransactionResponse> {
const user = await signer.getAddress();

// Get decimals for this specific ERC20
const token = await tokensApi.getToken({ address: deposit.tokenAddress });
const decimals = parseInt(token.data.decimals);

const data: ERC20TokenData = {
decimals,
token_address: deposit.tokenAddress,
};

const amount = parseUnits(deposit.amount, 0); // 0 to always use undecimalized value

// Approve whether an amount of token from an account can be spent by a third-party account
const tokenContract = IERC20__factory.connect(deposit.tokenAddress, signer);
const approveTransaction = await tokenContract.populateTransaction.approve(
config.ethConfiguration.coreContractAddress,
amount,
);
await signer.sendTransaction(approveTransaction);

const getSignableDepositRequest = {
user,
token: {
type: deposit.type,
data,
},
amount: amount.toString(),
};

const signableDepositResult = await depositsApi.getSignableDeposit({
getSignableDepositRequest,
});

// Perform encoding on asset details to get an assetType (required for stark contract request)
const encodingResult = await encodingApi.encodeAsset({
assetType: 'asset',
encodeAssetRequest: {
token: {
type: deposit.type,
data: {
token_address: deposit.tokenAddress,
},
},
},
});

const assetType = encodingResult.data.asset_type;
const starkPublicKey = signableDepositResult.data.stark_key;
const vaultId = signableDepositResult.data.vault_id;
const quantizedAmount = BigNumber.from(signableDepositResult.data.amount);

const coreContract = Core__factory.connect(
config.ethConfiguration.coreContractAddress,
signer,
);

return executeDepositERC20(
signer,
quantizedAmount,
assetType,
starkPublicKey,
vaultId,
coreContract,
);
}
99 changes: 99 additions & 0 deletions src/workflows/depositV4/depositERC721.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { Signer } from '@ethersproject/abstract-signer';
import { TransactionResponse } from '@ethersproject/providers';
import { DepositsApi, EncodingApi } from '../../api';
import { Core, Core__factory, IERC721__factory } from '../../contracts';
import { ERC721Token } from '../../types';
import { ImmutableXConfiguration } from '../../config';

interface ERC721TokenData {
token_id: string;
token_address: string;
}

async function executeDepositERC721(
signer: Signer,
tokenId: string,
assetType: string,
starkPublicKey: string,
vaultId: number,
contract: Core,
): Promise<TransactionResponse> {
const populatedTransaction = await contract.populateTransaction.depositNft(
starkPublicKey,
assetType,
vaultId,
tokenId,
);

return signer.sendTransaction(populatedTransaction);
}

export async function depositERC721WorkflowV4(
signer: Signer,
deposit: ERC721Token,
depositsApi: DepositsApi,
encodingApi: EncodingApi,
config: ImmutableXConfiguration,
): Promise<TransactionResponse> {
const user = await signer.getAddress();

const data: ERC721TokenData = {
token_address: deposit.tokenAddress,
token_id: deposit.tokenId,
};

const amount = '1';

const getSignableDepositRequest = {
user,
token: {
type: deposit.type,
data,
},
amount: amount.toString(),
};

const signableDepositResult = await depositsApi.getSignableDeposit({
getSignableDepositRequest,
});

// Perform encoding on asset details to get an assetType (required for stark contract request)
const encodingResult = await encodingApi.encodeAsset({
assetType: 'asset',
encodeAssetRequest: {
token: {
type: deposit.type,
data: {
token_address: deposit.tokenAddress,
token_id: deposit.tokenId,
},
},
},
});

const assetType = encodingResult.data.asset_type;
const starkPublicKey = signableDepositResult.data.stark_key;
const vaultId = signableDepositResult.data.vault_id;

const coreContract = Core__factory.connect(
config.ethConfiguration.coreContractAddress,
signer,
);

// Approve whether an amount of token from an account can be spent by a third-party account
const tokenContract = IERC721__factory.connect(deposit.tokenAddress, signer);
const operator = config.ethConfiguration.coreContractAddress;
const isApprovedForAll = await tokenContract.isApprovedForAll(user, operator);
if (!isApprovedForAll) {
await tokenContract.setApprovalForAll(operator, true);
}

return executeDepositERC721(
signer,
deposit.tokenId,
assetType,
starkPublicKey,
vaultId,
coreContract,
);
}
81 changes: 81 additions & 0 deletions src/workflows/depositV4/depositEth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Signer } from '@ethersproject/abstract-signer';
import { TransactionResponse } from '@ethersproject/providers';
import { DepositsApi, EncodingApi } from '../../api';
import { parseUnits } from '@ethersproject/units';
import { Core, Core__factory } from '../../contracts';
import { ETHAmount } from '../../types';
import { BigNumber } from '@ethersproject/bignumber';
import { ImmutableXConfiguration } from '../../config';

interface ETHTokenData {
decimals: number;
}

async function executeDepositEth(
signer: Signer,
amount: BigNumber,
assetType: string,
starkPublicKey: string,
vaultId: number,
contract: Core,
): Promise<TransactionResponse> {
const populatedTransaction = await contract.populateTransaction[
'deposit(uint256,uint256,uint256)'
](starkPublicKey, assetType, vaultId);

return signer.sendTransaction({ ...populatedTransaction, value: amount });
}

export async function depositEthWorkflowV4(
signer: Signer,
deposit: ETHAmount,
depositsApi: DepositsApi,
encodingApi: EncodingApi,
config: ImmutableXConfiguration,
): Promise<TransactionResponse> {
const user = await signer.getAddress();
const data: ETHTokenData = {
decimals: 18,
};
const amount = parseUnits(deposit.amount, 'wei');

const getSignableDepositRequest = {
user,
token: {
type: deposit.type,
data,
},
amount: amount.toString(),
};

const signableDepositResult = await depositsApi.getSignableDeposit({
getSignableDepositRequest,
});

const encodingResult = await encodingApi.encodeAsset({
assetType: 'asset',
encodeAssetRequest: {
token: {
type: deposit.type,
},
},
});

const assetType = encodingResult.data.asset_type;
const starkPublicKey = signableDepositResult.data.stark_key;
const vaultId = signableDepositResult.data.vault_id;

const coreContract = Core__factory.connect(
config.ethConfiguration.coreContractAddress,
signer,
);

return executeDepositEth(
signer,
amount,
assetType,
starkPublicKey,
vaultId,
coreContract,
);
}
3 changes: 3 additions & 0 deletions src/workflows/depositV4/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './depositEth';
export * from './depositERC20';
export * from './depositERC721';
Loading
Loading