picasso-sdk v0.3.196
picasso-sdk
This is Picasso IBC sdk for cosmos, ethereum, solana, and polkadot(will be updated soon)
How to use indexer Query
NOTE: To use the indexer API, you need to obtain the Hasura endpoint and secret key. Please contact the Picasso team for assistance.
1. Install Packages
npm install react graphql graphql-tag subscriptions-transport-ws picasso-sdk
- Please get hasura url and
2. Code example with react
usePicassoStatus.ts
import { useEffect, useState } from 'react';
import { type DocumentNode } from 'graphql';
import gql from 'graphql-tag';
import { type IbcEventsResponse } from 'picasso-sdk';
import { SubscriptionClient } from 'subscriptions-transport-ws';
type QueryKey = {
fromBlockHash?: { _eq: string };
sequence?: { _eq: number };
};
const HASURA_GRAPHQL_ENDPOINT = process.env.NEXT_PUBLIC_HASURA_URL || '';
const HASURA_ADMIN_SECRET = process.env.NEXT_PUBLIC_HASURA_PRIVATE_KEY || '';
const subscriptionQueryWithTxHash = gql`
subscription MySubscription(
$txHash: String!
$fromBlockHash: String_comparison_exp = {}
$sequence: String_comparison_exp = {}
) {
IbcEvents(where: { data: { _contains: { txHash: $txHash } }, fromBlockHash: $fromBlockHash, sequence: $sequence }) {
data
fromAssetId
fromAmount
fromAddress
fromBlockHash
fromChainId
fromFee
fromFeeAssetId
fromTimestamp
nextSequence
sequence
sourceChannel
status
timeout
toAddress
toAmount
toAssetId
toBlockHash
toChainId
toFee
toFeeAssetId
updatedAt
toTimestamp
type
timeout_height
}
}
`;
const subscriptionQueryWithoutTxHash = gql`
subscription MySubscription($fromBlockHash: String_comparison_exp = {}, $sequence: String_comparison_exp = {}) {
IbcEvents(where: { fromBlockHash: $fromBlockHash, sequence: $sequence }) {
data
fromAssetId
fromAmount
fromAddress
fromBlockHash
fromChainId
fromFee
fromFeeAssetId
fromTimestamp
nextSequence
sequence
sourceChannel
status
timeout
toAddress
toAmount
toAssetId
toBlockHash
toChainId
toFee
toFeeAssetId
updatedAt
toTimestamp
type
timeout_height
}
}
`;
export const usePicassoStatus = (txHash?: string, duration: number = 10000) => {
const [ibcEvent, setIbcEvent] = useState<Partial<IbcEventsResponse>>();
const [hopIndex, setHopIndex] = useState(-1);
const resetStatus = () => {
setIbcEvent(undefined);
setHopIndex(-1);
};
const subscribeToIbcEvents = (
client: SubscriptionClient,
variables: QueryKey & { txHash?: string },
subscriptionQuery: DocumentNode
) => {
if (hopIndex > 100) {
client.close();
return;
}
const subscription = client.request({ query: subscriptionQuery, variables }).subscribe({
next(data) {
console.log('Received data:', data);
const event = data?.data?.IbcEvents?.[0];
setIbcEvent(event);
if (event?.fromBlockHash !== ibcEvent?.fromBlockHash) {
setHopIndex(prev => prev + 1);
}
if (event?.toBlockHash && event?.nextSequence) {
const nextVariables = {
fromBlockHash: { _eq: event.toBlockHash },
sequence: { _eq: event.nextSequence }
};
subscribeToIbcEvents(client, nextVariables, subscriptionQueryWithoutTxHash);
} else if (event && ['TransferPending', 'send_packet'].every(v => event?.status !== v)) {
client.close();
console.log('Subscription stopped:', event);
}
},
error(err) {
console.error('Subscription error:', err);
},
complete() {
console.log('Subscription complete');
}
});
return subscription;
};
useEffect(() => {
if (!txHash) return;
resetStatus();
const client = new SubscriptionClient(
HASURA_GRAPHQL_ENDPOINT,
{
reconnect: true,
connectionParams: {
headers: {
'x-hasura-admin-secret': HASURA_ADMIN_SECRET
}
}
},
WebSocket
);
const initialVariables = { txHash };
const initialSubscription = subscribeToIbcEvents(client, initialVariables, subscriptionQueryWithTxHash);
if (ibcEvent && ibcEvent?.status !== 'TransferPending' && ibcEvent?.status !== 'send_packet') {
console.log('this has closed');
client.close();
initialSubscription?.unsubscribe();
const timer = setTimeout(() => {
hopIndex >= 0 && setHopIndex(-1);
setIbcEvent(undefined);
}, duration);
return () => {
clearTimeout(timer);
console.log('Subscription stopped!');
};
}
return () => {
initialSubscription?.unsubscribe();
client.close();
clearTimeout(duration);
};
}, [txHash, duration]);
return { hopIndex, ibcEvent, resetStatus };
};
Usage
const Stepper = () => {
const { ibcEvent, hopIndex, resetStatus } = usePicassoStatus('txHash...');
return <div>stepper..</div>;
};
How to use methods
Ethereum
const web3 = getWeb3('endpoint');
const AMOUNT = '10000000000000000'; // hardcoded example
// use txhash to track the transaction from indexer
const EthereumButton = () => {
//example : send ETH osmosis -> solana transfer via picasso pfm
// example txhash: ethereum -> picasso https://etherscan.io/tx/0x6c3fd9120cfe7825d98e41e5b71279cfd6543c3811d82e40545ca7e69a2d95ce
const ethereumToPicassoTransfer = async () => {
//TODO: add approval
const txHash = await ethereumTransfer({
web3,
amount: AMOUNT, // wei amount
assetId: 'ETH', // for PICA it is '0xbb63a9b64a80e9338b8ea298c51765e57c4f159c'
originAddress: '0x46762bdE09C1a1c566c3efD959368455Bf20c354',
destinationAddress: 'pica1ewm97t5qw3uutwd9qh0ydy007ymhl8qth56qlj',
channel: 2, // etheruem to picasso(cosmos) channel is 2
minimalDenom: 'ETH', // for PICA it is 'transfer/channel-2/ppica'
memo: '',
timeout: 240
});
console.log(txHash, 'txHash:Ethereum->Picasso');
};
// example txHash: ethereum -> archway https://etherscan.io/tx/0x332d9cd30af18245e5a70989f0e61a0f98594ca25baf159ab409223b808c4744
// send PICA from ethereum to osmosis
const ethereumPfmTransfer = async () => {
const approvedAmount = await getApprovedErc20(
web3,
'0x46762bdE09C1a1c566c3efD959368455Bf20c354',
'0xbb63a9b64a80e9338b8ea298c51765e57c4f159c'
); // PICA's erc20 address
if (new Big(approvedAmount || 0).lt(AMOUNT)) {
await approveErc20({
web3,
account: '0xbb63a9b64a80e9338b8ea298c51765e57c4f159c',
amount: AMOUNT,
erc20TokenAddress: '0x3432b6a60d23ca0dfca7761b7ab56459d9c964d0' // FXS's erc20 address
});
}
// ethereum memo should use escape code
const memoWithEscape = memoBuilder({
destChannel: 3, // picasso(cosmos)-> osmosis 's source channel is 3
destAddress: 'osmo1ewm97t5qw3uutwd9qh0ydy007ymhl8qtyn24d8' // osmosis address
})
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"');
// TODO: add approval
const txHash = await ethereumTransfer({
web3,
amount: '1000000000000000', // wei amount
assetId: 'ETH', // for PICA it is '0xbb63a9b64a80e9338b8ea298c51765e57c4f159c'
originAddress: '0x46762bdE09C1a1c566c3efD959368455Bf20c354', // ethereum address
destinationAddress: 'pica1ewm97t5qw3uutwd9qh0ydy007ymhl8qth56qlj', // picasso address
channel: 2, // etheruem to picasso(cosmos) channel is 2
minimalDenom: 'ETH', // for PICA it is 'transfer/channel-2/ppica'
memo: memoWithEscape
});
console.log(txHash, 'txHash:Ethereum->Osmosis');
};
return (
<>
<button onClick={ethereumToPicassoTransfer}>ethereumToPicassoTransfer</button>
<button onClick={ethereumPfmTransfer}>ethereumPfmTransfer</button>
</>
);
};
Solana
const SolanaButton = () => {
//example : osmosis -> picasso transfer
const solanaToPicassoTransfer = async () => {
//rewrite below code to use solanaTransfer method
const txHash = await solanaTransfer({
//write the parameters here
quantity: '1000000',
accountId: '1ryziZbFQW4fcWck9wW4vU4KD4qxPHKhmAht6pXPFWo', // solana address
destinationAddress: 'pica1ewm97t5qw3uutwd9qh0ydy007ymhl8qth56qlj', // picasso originAddress
configAssetId: 'EpZeFE5ngedGVug3s1VQK9987FEsCY512VtKUKmfzidM', // Solana OSMO mintAddress
sourceChannelId: 1, // solana -> picasso(cosmos) 's source channel is 1
configDenom: 'transfer/channel-1/transfer/channel-3/uosmo',
endpoint: 'https://mainnet.helius-rpc.com/?api-key=your-api-key', // helius rpc
timeout: getTimeOut(30).toNumber(),
memo: ''
});
console.log(txHash, 'txHash:Solana->Picasso');
};
// example: solana -> osmosis transfer via picasso pfm
const solanaPfmTransfer = async () => {
const txHash = await solanaTransfer({
//write the parameters here
quantity: '1000000',
accountId: '1ryziZbFQW4fcWck9wW4vU4KD4qxPHKhmAht6pXPFWo', // solana address
destinationAddress: 'pfm', // set pfm as hop address
configAssetId: 'EpZeFE5ngedGVug3s1VQK9987FEsCY512VtKUKmfzidM', // Solana OSMO mintAddress
sourceChannelId: 1, // solana -> picasso(cosmos) 's source channel is 1
configDenom: 'transfer/channel-1/transfer/channel-3/uosmo',
endpoint: 'https://mainnet.helius-rpc.com/?api-key=your-api-key', // helius rpc
timeout: getTimeOut(30).toNumber(),
memo: memoBuilder({
destChannel: 3, // picasso(cosmos)-> osmosis 's source channel is 3
destAddress: 'osmo1ewm97t5qw3uutwd9qh0ydy007ymhl8qtyn24d8' // osmosis address
}) // memo for pfm
});
console.log(txHash, 'txHash:Solana->Osmosis');
};
return (
<>
<button onClick={solanaToPicassoTransfer}>solanaToPicassoTransfer</button>
<button onClick={solanaPfmTransfer}>solanaPfmTransfer</button>
</>
);
};
Cosmos
const CosmosButton = () => {
//example : osmosis -> solana transfer via picasso pfm
const cosmosToSolanaTransfer = async () => {
const txHash = await cosmosTransfer({
sourceChannel: 1279,
sourceAddress: 'osmo1ewm97t5qw3uutwd9qh0ydy007ymhl8qtyn24d8', //osmosis fromAddress
destAddress: 'pica1ewm97t5qw3uutwd9qh0ydy007ymhl8qth56qlj', // picasso hopAddress
amount: '100000000', // minimal denom amount
assetId: 'uosmo', // coin or ibc denom
fee: 'auto', // set fee as auto
chainId: 'osmosis-1', // source chain id
rpc: 'https://osmosis-rpc.polkachu.com', // use chain rpc from chain registry
memo: memoBuilder({
destChannel: 71, // picasso(cosmos)-> solana 's source channel is 71
destAddress: '1ryziZbFQW4fcWck9wW4vU4KD4qxPHKhmAht6pXPFWo' // solana address
}) // memo for pfm
});
console.log(txHash, 'txHash:Osmosis->Solana');
};
// example : osmosis -> ethereum transfer via picasso pfm
const cosmosToEthereumTransfer = async () => {
const txHash = await cosmosTransfer({
sourceChannel: 1279,
sourceAddress: 'osmo1ewm97t5qw3uutwd9qh0ydy007ymhl8qtyn24d8', //osmosis fromAddress
destAddress: 'pica1ewm97t5qw3uutwd9qh0ydy007ymhl8qth56qlj', // picasso hopAddress
amount: '100000000', // minimal denom amount
assetId: 'uosmo', // coin or ibc denom
fee: 'auto', // set fee as auto
chainId: 'osmosis-1', // source chain id
rpc: 'https://osmosis-rpc.polkachu.com', // use chain rpc from chain registry
memo: memoBuilder({
destChannel: 52, // picasso(cosmos)->ethereum's source channel is 52
destAddress: '0x46762bdE09C1a1c566c3efD959368455Bf20c354' // ethereum address
}) //memo for pfm
});
console.log(txHash, 'txHash:Osmosis->Ethereum');
};
return (
<>
<button onClick={cosmosToSolanaTransfer}>cosmosToSolanaTransfer</button>
<button onClick={cosmosToEthereumTransfer}>cosmosToEthereumTransfer</button>
</>
);
};
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
4 months ago
5 months ago
7 months ago
7 months ago
7 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
5 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
8 months ago
8 months ago
8 months ago
7 months ago
8 months ago
7 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
8 months ago
8 months ago
8 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
10 months ago
9 months ago
9 months ago
10 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
9 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
10 months ago
10 months ago
10 months ago
10 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
10 months ago
9 months ago
9 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago