barterjs-sdk
Version:
Barter Network SDK
294 lines (271 loc) • 8.82 kB
text/typescript
import {
Account,
connect,
ConnectedWalletAccount,
KeyPair,
Near,
} from 'near-api-js';
import {
NearNetworkConfig,
TransferOutOptions,
} from '../../types/requestTypes';
import { MCS_CONTRACT_ADDRESS_SET } from '../../constants/addresses';
import { ChainId, ID_TO_CHAIN_ID } from '../../constants/chains';
import {
ADD_MCS_TOKEN_TO_CHAIN,
ADD_NATIVE_TO_CHAIN,
TRANSFER_OUT_NATIVE,
TRANSFER_OUT_TOKEN,
} from '../../constants/near_method_names';
import BN from 'bn.js';
import { ChangeFunctionCallOptions } from 'near-api-js/lib/account';
import { IMapCrossChainService } from '../interfaces/IMapCrossChainService';
import { hexToDecimalArray } from '../../utils';
import {
BarterTransactionReceipt,
BarterTransactionResponse,
} from '../../types/responseTypes';
import {
adaptNearReceipt,
assembleNearTransactionResponse,
} from '../../utils/responseUtil';
import { FinalExecutionOutcome } from 'near-api-js/lib/providers';
import { NearProviderType } from '../../types/paramTypes';
export class NearCrossChainService implements IMapCrossChainService {
provider: NearProviderType;
/**
* we treat account as class member cuz to initialize a near account, async is required
* @param provider
*/
constructor(provider: NearProviderType) {
this.provider = provider;
}
/**
* transfer out token(not native coin) from source chain to designated token on target chain
* @param fromAddress
* @param tokenAddress input token address
* @param amount amount in minimal unit
* @param toAddress target chain receiving address
* @param toChainId target chain id
* @param options see {@link TransferOutOptions} for more detail
*/
async doTransferOutToken(
fromAddress: string,
tokenAddress: string,
amount: string,
toAddress: string,
toChainId: string,
options: TransferOutOptions
): Promise<BarterTransactionResponse> {
let mcsAccountId: string;
let account: Account | ConnectedWalletAccount;
if (this.provider instanceof NearNetworkConfig) {
// get mcs contract address
mcsAccountId =
this.provider.networkId === 'testnet'
? MCS_CONTRACT_ADDRESS_SET[ChainId.NEAR_TESTNET]
: '';
// prep near connection
const near: Near = await connect(this.provider);
account = await near.account(this.provider.fromAccount);
} else {
mcsAccountId = this.provider.getAccountId().endsWith('testnet')
? MCS_CONTRACT_ADDRESS_SET[ChainId.NEAR_TESTNET]
: '';
account = this.provider.account();
}
try {
// the receiving address on Near need be in the format of number array as input
const decimalArrayAddress: number[] = hexToDecimalArray(
toAddress,
toChainId
);
// contract call option
const nearCallOptions: ChangeFunctionCallOptions = {
contractId: mcsAccountId,
methodName: TRANSFER_OUT_TOKEN,
args: {
token: tokenAddress,
to: decimalArrayAddress,
amount: amount,
to_chain: toChainId,
},
attachedDeposit: new BN(amount, 10),
};
// manual input gas if necessary
if (options.gas != undefined) {
nearCallOptions.gas = new BN(options.gas, 10);
}
const executionOutcome: FinalExecutionOutcome =
await this._doNearFunctionCall(account, nearCallOptions);
return assembleNearTransactionResponse(executionOutcome);
} catch (error) {
throw error;
}
}
/**
* transfer out native coin from source chain to designated token on target chain
* @param fromAddress
* @param toAddress target chain receiving address
* @param toChainId target chain id
* @param amount amount to bridge in minimal unit
* @param options see {@link TransferOutOptions} for more detail
*/
async doTransferOutNative(
fromAddress: string,
toAddress: string,
toChainId: string,
amount: string,
options: TransferOutOptions
): Promise<BarterTransactionResponse> {
let mcsAccountId: string;
let account: Account | ConnectedWalletAccount;
if (this.provider instanceof NearNetworkConfig) {
mcsAccountId =
this.provider.networkId === 'testnet'
? MCS_CONTRACT_ADDRESS_SET[ChainId.NEAR_TESTNET]
: '';
const near: Near = await connect(this.provider);
account = await near.account(this.provider.fromAccount);
} else {
mcsAccountId = this.provider.getAccountId().endsWith('testnet')
? MCS_CONTRACT_ADDRESS_SET[ChainId.NEAR_TESTNET]
: '';
account = this.provider.account();
}
try {
const decimalArrayAddress: number[] = hexToDecimalArray(
toAddress,
toChainId
);
const nearCallOptions: ChangeFunctionCallOptions = {
contractId: mcsAccountId,
methodName: TRANSFER_OUT_NATIVE,
args: {
to: decimalArrayAddress,
to_chain: Number.parseInt(toChainId),
},
attachedDeposit: new BN(amount, 10),
};
if (options.gas != undefined) {
nearCallOptions.gas = new BN(options.gas, 10);
}
const executionOutcome: FinalExecutionOutcome =
await this._doNearFunctionCall(account, nearCallOptions);
return assembleNearTransactionResponse(executionOutcome);
} catch (error) {
throw error;
}
}
/**
* add tochain to allowed transfer out chains.
* @param toChainId
*/
public async addNativeToChain(toChainId: string) {
if (this.provider instanceof NearNetworkConfig) {
const mcsAccountId: string =
this.provider.networkId === 'testnet'
? MCS_CONTRACT_ADDRESS_SET[ChainId.NEAR_TESTNET]
: '';
const near: Near = await connect(this.provider);
const account = await near.account(this.provider.fromAccount);
const nearCallOptions: ChangeFunctionCallOptions = {
contractId: mcsAccountId,
methodName: ADD_NATIVE_TO_CHAIN,
args: {
to_chain: toChainId,
},
};
return await this._doNearFunctionCall(account, nearCallOptions);
}
}
public async addTokenToChain(tokenAddress: string, toChainId: number) {
if (this.provider instanceof NearNetworkConfig) {
const mcsAccountId: string =
this.provider.networkId === 'testnet'
? MCS_CONTRACT_ADDRESS_SET[ChainId.NEAR_TESTNET]
: '';
const near: Near = await connect(this.provider);
const account = await near.account(this.provider.fromAccount);
const nearCallOptions: ChangeFunctionCallOptions = {
contractId: mcsAccountId,
methodName: ADD_MCS_TOKEN_TO_CHAIN,
args: {
token: tokenAddress,
to_chain: toChainId,
},
};
return await this._doNearFunctionCall(account, nearCallOptions);
}
}
/**
* call near smart contract
* @param account
* @param options
* @private
*/
private async _doNearFunctionCall(
account: Account | ConnectedWalletAccount,
options: ChangeFunctionCallOptions
): Promise<FinalExecutionOutcome> {
let outcome: FinalExecutionOutcome;
try {
outcome = await account.functionCall(options);
} catch (e) {
console.log(e);
}
return outcome!;
}
doDepositOutToken(
tokenAddress: string,
from: string,
to: string,
amount: string,
options?: TransferOutOptions
): Promise<string> {
return Promise.resolve('');
}
gasEstimateTransferOutNative(
fromAddress: string,
toAddress: string,
toChainId: string,
amount: string,
options?: TransferOutOptions
): Promise<string> {
return Promise.resolve('');
}
gasEstimateTransferOutToken(
fromAddress: string,
tokenAddress: string,
amount: string,
toAddress: string,
toChainId: string,
options?: TransferOutOptions
): Promise<string> {
return Promise.resolve('');
}
async addFungibleTokenToChain(
tokenAddress: string,
toChainId: string
): Promise<void> {
if (this.provider instanceof NearNetworkConfig) {
const mcsAccountId: string =
this.provider.networkId === 'testnet'
? MCS_CONTRACT_ADDRESS_SET[ChainId.NEAR_TESTNET]
: '';
const near: Near = await connect(this.provider);
const account = await near.account(this.provider.fromAccount);
const nearCallOptions: ChangeFunctionCallOptions = {
contractId: mcsAccountId,
methodName: 'add_fungible_token_to_chain',
args: {
token: tokenAddress,
to_chain: toChainId,
},
gas: new BN('300000000000000', 10),
};
const executionOutcome: FinalExecutionOutcome =
await this._doNearFunctionCall(account, nearCallOptions);
}
}
}