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

CORE-2034 add register and complete withdraw workflow #438

Merged
merged 3 commits into from
Feb 28, 2024
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
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## [3.5.0] - 2024-02-28

### Added

- Update `completeWithdrawal`:
- If the user has only v4 balance, `completeWithdrawal` will withdraw v4 balance.
- If the user has v3 balance, `completeWithdrawal` will try to withdraw both v3 and v4 balance.
- If the user has v3 balance, and is not registered on-chain, `completeWithdrawal` will try to register the user and then withdraw both v3 and v4 balance.
- If the v3 balance is not zero for an NFT, `completeWithdrawal` will only try to withdraw v3 balance.
- v3 balance refers to withdrawal prepared using /v1/withdrawal API.
- v4 balance refers to withdrawal prepared using /v2/withdrawal API.

## [3.4.0] - 2024-02-27

### Added
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@imtbl/core-sdk",
"version": "3.4.0",
"version": "3.5.0",
"description": "Immutable Core SDK",
"main": "dist/index.cjs.js",
"module": "dist/index.es.js",
Expand Down
37 changes: 36 additions & 1 deletion src/workflows/withdrawal/completeERC20Withdrawal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import {
Registration__factory,
RegistrationV4__factory,
} from '../../contracts';
import { ERC20Token } from '../../types';
import { ERC20Token, WalletConnection } from '../../types';
import {
getSignableRegistrationOnchain,
isRegisteredOnChainWorkflow,
signRegisterEthAddress,
} from '../registration';
import { getEncodeAssetInfo } from './getEncodeAssetInfo';

Expand Down Expand Up @@ -153,3 +154,37 @@ export async function completeAllERC20WithdrawalWorkflow(

return signer.sendTransaction(populatedTransaction);
}

export async function registerAndCompleteAllERC20WithdrawalWorkflow(
walletConnection: WalletConnection,
ethAddress: string,
starkPublicKey: string,
token: ERC20Token,
encodingApi: EncodingApi,
config: ImmutableXConfiguration,
): Promise<TransactionResponse> {
const assetType = await getEncodeAssetInfo('asset', 'ERC20', encodingApi, {
token_address: token.tokenAddress,
});

const registrationContract = RegistrationV4__factory.connect(
config.ethConfiguration.registrationContractAddress,
walletConnection.ethSigner,
);

const starkSignature = await signRegisterEthAddress(
walletConnection.starkSigner,
ethAddress,
starkPublicKey,
);

const populatedTransaction =
await registrationContract.populateTransaction.registerAndWithdrawAll(
ethAddress,
starkPublicKey,
starkSignature,
assetType.asset_id,
);

return walletConnection.ethSigner.sendTransaction(populatedTransaction);
}
129 changes: 127 additions & 2 deletions src/workflows/withdrawal/completeERC721Withdrawal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ import { EncodingApi, MintsApi, UsersApi } from '../../api';
import {
Core,
Core__factory,
CoreV4__factory,
Registration,
Registration__factory,
CoreV4__factory,
RegistrationV4__factory,
} from '../../contracts';
import * as encUtils from 'enc-utils';
import { ERC721Token } from '../../types';
import { ERC721Token, WalletConnection } from '../../types';
import { getEncodeAssetInfo } from './getEncodeAssetInfo';
import {
getSignableRegistrationOnchain,
isRegisteredOnChainWorkflow,
signRegisterEthAddress,
} from '../registration';
import { TransactionResponse } from '@ethersproject/providers';
import { ImmutableXConfiguration } from '../../config';
Expand Down Expand Up @@ -330,6 +332,72 @@ async function completeERC721WithdrawalV2(
return signer.sendTransaction(populatedTransaction);
}

async function registerAndCompleteMintableERC721WithdrawalV2(
signer: Signer,
ethAddress: string,
starkPublicKey: string,
starkSignature: string,
token: MintableERC721Withdrawal,
encodingApi: EncodingApi,
config: ImmutableXConfiguration,
) {
const assetType = await getEncodeAssetInfo(
'mintable-asset',
'ERC721',
encodingApi,
{
id: token.data.id,
token_address: token.data.tokenAddress,
...(token.data.blueprint && { blueprint: token.data.blueprint }),
},
);
const mintingBlob = getMintingBlob(token);
const contract = RegistrationV4__factory.connect(
config.ethConfiguration.registrationContractAddress,
signer,
);

const populatedTransaction =
await contract.populateTransaction.registerWithdrawAndMint(
ethAddress,
starkPublicKey,
starkSignature,
assetType.asset_type,
mintingBlob,
);
return signer.sendTransaction(populatedTransaction);
}

async function registerAndCompleteERC721WithdrawalV2(
signer: Signer,
ethAddress: string,
starkPublicKey: string,
starkSignature: string,
token: ERC721Token,
encodingApi: EncodingApi,
config: ImmutableXConfiguration,
) {
const assetType = await getEncodeAssetInfo('asset', 'ERC721', encodingApi, {
token_id: token.tokenId,
token_address: token.tokenAddress,
});

const contract = RegistrationV4__factory.connect(
config.ethConfiguration.registrationContractAddress,
signer,
);

const populatedTransaction =
await contract.populateTransaction.registerAndWithdrawNft(
ethAddress,
starkPublicKey,
starkSignature,
assetType.asset_type,
token.tokenId,
);
return signer.sendTransaction(populatedTransaction);
}

export async function completeERC721WithdrawalV2Workflow(
signer: Signer,
ownerKey: string,
Expand Down Expand Up @@ -375,3 +443,60 @@ export async function completeERC721WithdrawalV2Workflow(
throw error; // unable to recover from any other kind of error
});
}

export async function registerAndCompleteERC721WithdrawalWorkflow(
walletConnection: WalletConnection,
ethAddress: string,
starkPublicKey: string,
token: ERC721Token,
encodingApi: EncodingApi,
mintsApi: MintsApi,
config: ImmutableXConfiguration,
): Promise<TransactionResponse> {
const starkSignature = await signRegisterEthAddress(
walletConnection.starkSigner,
ethAddress,
starkPublicKey,
);
const tokenAddress = token.tokenAddress;
const tokenId = token.tokenId;

return await mintsApi
.getMintableTokenDetailsByClientTokenId({
tokenAddress,
tokenId,
})
.then(async mintableToken =>
registerAndCompleteMintableERC721WithdrawalV2(
walletConnection.ethSigner,
ethAddress,
starkPublicKey,
starkSignature,
{
type: 'ERC721',
data: {
id: tokenId,
tokenAddress: tokenAddress,
blueprint: mintableToken.data.blueprint,
},
},
encodingApi,
config,
),
)
.catch(error => {
if (error.response?.status === 404) {
// token is already minted on L1
return registerAndCompleteERC721WithdrawalV2(
walletConnection.ethSigner,
ethAddress,
starkPublicKey,
starkSignature,
token,
encodingApi,
config,
);
}
throw error; // unable to recover from any other kind of error
});
}
33 changes: 33 additions & 0 deletions src/workflows/withdrawal/completeEthWithdrawal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import {
import {
getSignableRegistrationOnchain,
isRegisteredOnChainWorkflow,
signRegisterEthAddress,
} from '../registration';
import { getEncodeAssetInfo } from './getEncodeAssetInfo';
import { WalletConnection } from 'src/types';

async function executeRegisterAndWithdrawEth(
signer: Signer,
Expand Down Expand Up @@ -143,3 +145,34 @@ export async function completeAllEthWithdrawalWorkflow(

return signer.sendTransaction(populatedTransaction);
}

export async function registerAndCompleteAllEthWithdrawalWorkflow(
walletConnection: WalletConnection,
ethAddress: string,
starkPublicKey: string,
encodingApi: EncodingApi,
config: ImmutableXConfiguration,
): Promise<TransactionResponse> {
const assetType = await getEncodeAssetInfo('asset', 'ETH', encodingApi);

const registrationContract = RegistrationV4__factory.connect(
config.ethConfiguration.registrationContractAddress,
walletConnection.ethSigner,
);

const starkSignature = await signRegisterEthAddress(
walletConnection.starkSigner,
ethAddress,
starkPublicKey,
);

const populatedTransaction =
await registrationContract.populateTransaction.registerAndWithdrawAll(
ethAddress,
starkPublicKey,
starkSignature,
assetType.asset_id,
);

return walletConnection.ethSigner.sendTransaction(populatedTransaction);
}
46 changes: 45 additions & 1 deletion src/workflows/withdrawal/completeWithdrawal.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import { Signer } from '@ethersproject/abstract-signer';
import { TransactionResponse } from '@ethersproject/providers';
import { AnyToken } from 'src/types';
import { AnyToken, WalletConnection } from 'src/types';
import { EncodingApi, MintsApi, UsersApi } from 'src/api';
import { ImmutableXConfiguration } from 'src/config';
import {
completeAllEthWithdrawalWorkflow,
completeEthWithdrawalV1Workflow,
completeEthWithdrawalV2Workflow,
registerAndCompleteAllEthWithdrawalWorkflow,
} from './completeEthWithdrawal';
import {
completeAllERC20WithdrawalWorkflow,
completeERC20WithdrawalV1Workflow,
completeERC20WithdrawalV2Workflow,
registerAndCompleteAllERC20WithdrawalWorkflow,
} from './completeERC20Withdrawal';
import {
completeERC721WithdrawalV1Workflow,
completeERC721WithdrawalV2Workflow,
registerAndCompleteERC721WithdrawalWorkflow,
} from './completeERC721Withdrawal';

export async function completeWithdrawalV1Workflow(
Expand Down Expand Up @@ -124,3 +127,44 @@ export async function completeAllWithdrawalWorkflow(
);
}
}

export async function registerAndCompleteAllWithdrawalWorkflow(
walletConnection: WalletConnection,
ethAddress: string,
starkPublicKey: string,
token: AnyToken,
encodingApi: EncodingApi,
mintsApi: MintsApi,
config: ImmutableXConfiguration,
): Promise<TransactionResponse> {
switch (token.type) {
case 'ETH':
return registerAndCompleteAllEthWithdrawalWorkflow(
walletConnection,
ethAddress,
starkPublicKey,
encodingApi,
config,
);
case 'ERC20':
return registerAndCompleteAllERC20WithdrawalWorkflow(
walletConnection,
ethAddress,
starkPublicKey,
token,
encodingApi,
config,
);
case 'ERC721':
// for ERC721, if the v3 balance > 0, then the v4 balance is 0
return registerAndCompleteERC721WithdrawalWorkflow(
walletConnection,
ethAddress,
starkPublicKey,
token,
encodingApi,
mintsApi,
config,
);
}
}
2 changes: 2 additions & 0 deletions src/workflows/withdrawal/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export * from './prepareWithdrawal';
export * from './completeWithdrawal';
export * from './completeERC20Withdrawal';
export * from './completeERC721Withdrawal';
export * from './completeEthWithdrawal';
export * from './getWithdrawalBalance';
12 changes: 10 additions & 2 deletions src/workflows/workflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import {
completeAllWithdrawalWorkflow,
completeWithdrawalV1Workflow,
completeWithdrawalV2Workflow,
registerAndCompleteAllWithdrawalWorkflow,
} from './withdrawal/completeWithdrawal';
import { BigNumber } from 'ethers';
import { getWithdrawalBalanceWorkflow } from './withdrawal/getWithdrawalBalance';
Expand Down Expand Up @@ -311,7 +312,6 @@ export class Workflows {
const starkPublicKey = await walletConnection.starkSigner.getAddress();

if (majorContractVersion === 3) {
const starkPublicKey = await walletConnection.starkSigner.getAddress();
return completeWithdrawalV1Workflow(
walletConnection.ethSigner,
starkPublicKey,
Expand Down Expand Up @@ -359,7 +359,15 @@ export class Workflows {
this.config,
);
}
throw new Error('User unregistered');
return registerAndCompleteAllWithdrawalWorkflow(
walletConnection,
ethAddress,
starkPublicKey,
token,
this.encodingApi,
this.mintsApi,
this.config,
);
}
if (v4Balance.gt(0)) {
return completeWithdrawalV2Workflow(
Expand Down
Loading