Skip to content

Commit

Permalink
Merge pull request #14 from router-protocol/near
Browse files Browse the repository at this point in the history
Near wallets integration
  • Loading branch information
soumyaRouterP authored Oct 17, 2024
2 parents ff2fc7c + 80a2fbf commit b0a507d
Show file tree
Hide file tree
Showing 34 changed files with 4,073 additions and 272 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export default function App({ Component, pageProps }) {
| AlephZero | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Sui | :small_orange_diamond: | :x: | :x: | :x: | :x: |
| Cosmos | :x: | :x: | :x: | :x: | :x: |
| Near | :x: | :x: | :x: | :x: | :x: |
| Near | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Bitcoin | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Casper | :x: | :x: | :x: | :x: | :x: |
| Ton | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Expand Down
47 changes: 47 additions & 0 deletions example-next/src/app/wallets/mynearwallet/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use client';

import { useConnect, useWallets } from '@tangled3/react';
import { useEffect } from 'react';

export default function NearPage() {
const { connect, isLoading, error } = useConnect();
const wallets = useWallets();

const getNearContractId = (chainType: unknown) => {
// TODO: add contractId for both 'testnet' & 'mainnet'
if (chainType === 'near') {
return 'usdt.tether-token.near'; // For testnet: 'routetoken.i-swap.testnet'
}
};

useEffect(() => {
if (wallets.near.length > 0) {
connect(
{ walletId: 'my-near-wallet', chainType: 'near', nearContractId: getNearContractId('near') },
{
onSuccess: () => {
window.location.href = '/';
},
onError: (err) => {
console.error('Failed to connect:', err);
},
},
);
}
}, [wallets]);

if (isLoading) {
return <div>Connecting to wallet...</div>;
}

if (error) {
return <div>Error connecting to wallet: {error.message}</div>;
}

return (
<div>
<h1 className='text-lg font-semibold'>Oh, I see you're trying to connect to My Near Wallet.</h1>
<h2 className='text-center text-neutral-300 mt-2'>Please wait...</h2>
</div>
);
}
1 change: 1 addition & 0 deletions example-next/src/components/Providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const Providers = ({ children }: { children: ReactNode }) => {
twaReturnUrl: `${window.location.origin}/` as `${string}://${string}`,
projectId: '41980758771052df3f01be0a46f172a5',
tonconnectManifestUrl: `${window.location.origin}/tonconnect-manifest.json`,
nearNetwork: 'mainnet',
bitcoinNetwork: 'mainnet',
}}
>
Expand Down
14 changes: 14 additions & 0 deletions example-next/src/components/Tokens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,20 @@ const tokens: TokenMetadata[] = [
symbol: 'USDT',
chainId: '-239',
},
{
address: ETH_ADDRESS,
decimals: 24,
name: 'NEAR',
symbol: 'NEAR',
chainId: '397',
},
{
address: 'usdt.tether-token.near',
decimals: 6,
name: 'Tether USD',
symbol: 'USDt',
chainId: '397',
},
];

export default Tokens;
11 changes: 10 additions & 1 deletion example-next/src/components/WalletList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ const WalletList = () => {
const wallets = useWallets();
const { connect } = useConnect();

const getNearContractId = (chainType: unknown) => {
// TODO: add contractId for both 'testnet' & 'mainnet'
if (chainType === 'near') {
return 'usdt.tether-token.near'; // For testnet: 'routetoken.i-swap.testnet'
}
};

return (
<div className='max-h-[60rem] overflow-scroll bg-neutral-900'>
<h2 className='text-xl font-bold sticky top-0 bg-black'>WALLETS</h2>
Expand Down Expand Up @@ -41,7 +48,9 @@ const WalletList = () => {
<td className='px-4 py-2'>
{wallet.installed ? (
<button
onClick={() => connect({ walletId: wallet.id, chainType })}
onClick={() =>
connect({ walletId: wallet.id, chainType, nearContractId: getNearContractId(chainType) })
}
className='bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded'
>
connect
Expand Down
2 changes: 1 addition & 1 deletion example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@
"tailwindcss": "^3.4.3",
"typescript": "^5.2.2",
"vite": "^5.2.0",
"vite-plugin-node-polyfills": "^0.22.0"
"vite-plugin-node-polyfills": "^0.17.0"
}
}
1 change: 1 addition & 0 deletions example/src/components/Providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const Providers = ({ children }: { children: ReactNode }) => {

twaReturnUrl: `${window.location.origin}/` as `${string}://${string}`,
projectId: '41980758771052df3f01be0a46f172a5',
nearNetwork: 'mainnet',
tonconnectManifestUrl: `${window.location.origin}/tonconnect-manifest.json`,
bitcoinNetwork: 'mainnet',
}}
Expand Down
2 changes: 1 addition & 1 deletion packages/react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export default function App({ Component, pageProps }) {
| AlephZero | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: |
| Sui | :small_orange_diamond: | :x: | :x: | :x: | :x: |
| Cosmos | :x: | :x: | :x: | :x: | :x: |
| Near | :x: | :x: | :x: | :x: | :x: |
| Near | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Bitcoin | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Casper | :x: | :x: | :x: | :x: | :x: |
| Ton | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Expand Down
7 changes: 7 additions & 0 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
"@cosmos-kit/xdefi": "^2.10.2",
"@mysten/dapp-kit": "^0.14.14",
"@mysten/sui": "^1.4.0",
"@near-wallet-selector/core": "^8.9.13",
"@near-wallet-selector/ethereum-wallets": "^8.9.13",
"@near-wallet-selector/my-near-wallet": "^8.9.13",
"@near-wallet-selector/near-mobile-wallet": "^8.9.13",
"@near-wallet-selector/wallet-connect": "^8.9.13",
"@nightlylabs/wallet-selector-polkadot": "^0.2.6",
"@polkadot/api": "^12.3.1",
"@polkadot/api-contract": "^12.3.1",
Expand All @@ -57,7 +62,9 @@
"@tronweb3/tronwallet-abstract-adapter": "^1.1.6",
"@tronweb3/tronwallet-adapters": "^1.2.1",
"@wagmi/core": "^2.11.0",
"@web3modal/wagmi": "^5.1.9",
"detect-browser": "^5.3.0",
"near-api-js": "^5.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"tronweb": "6.0.0-beta.4",
Expand Down
17 changes: 16 additions & 1 deletion packages/react/src/actions/getBalances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { multicall } from '@wagmi/core';
import { Address as EVMAddress, erc20Abi } from 'viem';
import { trc20Abi } from '../constants/abi/trc20.js';
import { tronMulticallAbi } from '../constants/abi/tronMulticall.js';
import { ChainData, ConnectionOrConfig } from '../types/index.js';
import { ChainData, ConnectionOrConfig, OtherChainData } from '../types/index.js';
import { viewMethodOnNear } from './near/readCalls.js';

export const getBalances = async (
tokens: { address: string }[],
Expand Down Expand Up @@ -119,5 +120,19 @@ export const getBalances = async (
return balances;
}

if (chain.type === 'near') {
const balances: Record<string, bigint> = {};

for (const token of tokens) {
balances[token.address] = BigInt(
await viewMethodOnNear(chain as OtherChainData<'near'>, token.address, 'ft_balance_of', {
account_id: account,
}),
);
}

return balances;
}

throw new Error('Unsupported chain type');
};
37 changes: 36 additions & 1 deletion packages/react/src/actions/getToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@ import { Address as EVMAddress } from 'viem';
import { trc20Abi } from '../constants/abi/trc20.js';
import { ETH_ADDRESS, SOL_ADDRESS } from '../constants/index.js';
import { TokenMetadata } from '../hooks/useToken.js';
import { ChainData, ChainId, ChainType, ConnectionOrConfig, GetTokenMetadataParams } from '../types/index.js';
import {
ChainData,
ChainId,
ChainType,
ConnectionOrConfig,
GetTokenMetadataParams,
OtherChainData,
} from '../types/index.js';
import { areTokensEqual } from '../utils/index.js';
import { getAlephZeroTokenBalanceAndAllowance, getAlephZeroTokenMetadata } from './alephZero/getAlephZeroToken.js';
import { getBitcoinApiConfig } from './bitcoin/bitcoinApiConfig.js';
import { fetchBalance as fetchBitcoinBalance } from './bitcoin/transaction.js';
import { getCosmosTokenBalanceAndAllowance, getCosmosTokenMetadata } from './cosmos/getCosmosToken.js';
import { getEVMTokenBalanceAndAllowance, getEVMTokenMetadata } from './evm/getEVMToken.js';
import { viewMethodOnNear } from './near/readCalls.js';
import { getSolanaTokenBalanceAndAllowance } from './solana/getSolanaToken.js';
import { getTonTokenBalanceAndAllowance, getTonTokenMetadata } from './ton/getTonToken.js';

Expand Down Expand Up @@ -116,6 +124,22 @@ export const getTokenMetadata = async ({ token, chain, config }: GetTokenMetadat
chainId: chain.id,
};
}

if (chain.type === 'near') {
if (areTokensEqual(token, ETH_ADDRESS)) {
return { ...chain.nativeCurrency, address: ETH_ADDRESS, chainId: chain.id };
}
const res = await viewMethodOnNear(chain as OtherChainData<'near'>, token, 'ft_metadata');

return {
name: res.name,
symbol: res.symbol,
decimals: res.decimals,
address: token,
chainId: chain.id,
};
}

throw new Error('Chain type not supported');
};

Expand Down Expand Up @@ -247,5 +271,16 @@ export const getTokenBalanceAndAllowance = (async (params) => {
return { balance, allowance: 0n };
}

if (chain.type === 'near') {
const balance = await viewMethodOnNear(chain as OtherChainData<'near'>, token, 'ft_balance_of', {
account_id: account,
});
const allowance = await viewMethodOnNear(chain as OtherChainData<'near'>, token, 'storage_balance_of', {
account_id: account,
});

return { balance, allowance };
}

throw new Error('Chain type not supported');
}) as GetTokenBalanceAndAllowanceFunction;
14 changes: 12 additions & 2 deletions packages/react/src/actions/getTransactionReceipt.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { getTransactionReceipt as getEVMTransactionReceipt } from '@wagmi/core';
import { ChainData, ChainType, ConnectionOrConfig, TransactionReceipt } from '../types/index.js';
import { ChainData, ChainType, ConnectionOrConfig, OtherChainData, TransactionReceipt } from '../types/index.js';
import { getBitcoinApiConfig } from './bitcoin/bitcoinApiConfig.js';
import { fetchTransaction } from './bitcoin/transaction.js';
import { getNearProvider } from './near/readCalls.js';
import { TransactionParams } from './waitForTransaction.js';

export type GetTransactionReceiptOverrides<C extends ChainType = ChainType> = C extends 'solana'
? {
maxSupportedTransactionVersion: number;
}
: any;
: C extends 'near'
? { accountAddress: string }
: any;

Check warning on line 14 in packages/react/src/actions/getTransactionReceipt.ts

View workflow job for this annotation

GitHub Actions / build (20)

Unexpected any. Specify a different type

export type GetTransactionReceiptParams<CData extends ChainData> = {
transactionParams: TransactionParams<CData['type']>;
Expand Down Expand Up @@ -140,5 +143,12 @@ export const getTransactionReceipt = (async ({
return result;
}

if (chain.type === 'near') {
const { txHash } = transactionParams as TransactionParams<'near'>;

const provider = await getNearProvider(chain as OtherChainData<'near'>);
return await provider.txStatus(txHash, overrides.accountAddress);
}

throw new Error('Chain type not supported');
}) as GetTransactionReceiptFunction;
28 changes: 28 additions & 0 deletions packages/react/src/actions/near/readCalls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { providers } from 'near-api-js';
import { OtherChainData } from '../../types/index.js';

export const THIRTY_TGAS = '30000000000000';
export const NO_DEPOSIT = '0';

export function getNearProvider(chain: OtherChainData<'near'>) {
return new providers.JsonRpcProvider({ url: chain.rpcUrls.default.http[0] });
}

export async function viewMethodOnNear(chain: OtherChainData<'near'>, token: string, method: string, args = {}) {
const provider = getNearProvider(chain);

try {
let res = await provider.query({
request_type: 'call_function',
account_id: token,
method_name: method,
args_base64: Buffer.from(JSON.stringify(args)).toString('base64'),
finality: 'optimistic',
});
res = JSON.parse(Buffer.from(res.result).toString());
return res;
} catch (error) {
console.error('Error calling viewMethodOnNear:', error);
throw error;
}
}
55 changes: 49 additions & 6 deletions packages/react/src/actions/sendTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Address as EVMAddress } from 'viem';
import { ChainData, ChainType, ConnectionOrConfig } from '../types/index.js';
import { WalletInstance } from '../types/wallet.js';
import { signBitcoinTx } from './bitcoin/transaction.js';
import { NO_DEPOSIT, THIRTY_TGAS } from './near/readCalls.js';

export type SendTransactionParams<CData extends ChainData> = {
chain: CData;
Expand Down Expand Up @@ -47,14 +48,33 @@ export type TransactionArgs<CType extends ChainType> = CType extends 'evm' | 'tr
stateInit?: string;
};
}
: CType extends 'cosmos'
: CType extends 'near'
? {
messages: readonly MsgExecuteContractEncodeObject[];
memo?: string;
nearArgs: {
transactionType:
| 'CreateAccount'
| 'DeployContract'
| 'FunctionCall'
| 'Transfer'
| 'Stake'
| 'AddKey'
| 'DeleteKey'
| 'DeleteAccount';
signerId?: string;
methodName?: string;
args?: object;
gas?: string;
deposit?: string;
};
}
: CType extends 'bitcoin'
? { memo: string; feeRate?: number }
: never;
: CType extends 'cosmos'
? {
messages: readonly MsgExecuteContractEncodeObject[];
memo?: string;
}
: CType extends 'bitcoin'
? { memo: string; feeRate?: number }
: never;

type SendTransactionReturnType<C extends ChainType> = C extends 'alephZero'
? {
Expand Down Expand Up @@ -242,5 +262,28 @@ export const sendTransactionToChain = (async ({ chain, to, from, value, args, co
return { txHash };
}

if (chain.type === 'near') {
const { nearArgs } = args as TransactionArgs<'near'>;

// sendTransaction to NEAR chain
const tx = await config.connector.signAndSendTransaction({
receiverId: to,
signerId: nearArgs.signerId,
actions: [
{
type: nearArgs.transactionType,
params: {
methodName: nearArgs.methodName,
args: nearArgs.args,
gas: nearArgs.gas ?? THIRTY_TGAS,
deposit: nearArgs.transactionType === 'Transfer' ? value : nearArgs.deposit ?? NO_DEPOSIT,
},
},
],
});

return { txHash: tx.transaction.hash };
}

throw new Error('Chain not supported');
}) as SendTransactionToChainFunction;
Loading

0 comments on commit b0a507d

Please sign in to comment.