Integrate Drop
Drop Protocol and its dAssets can be integrated into external UIs and apps on Neutron and any other IBC-connected chain. This page describes how to integrate Drop.
Overview
It is possible to integrate Drop in several ways:
- Use the auto-generated TypeScript client for smart contracts to interact with Drop Protocol.
- Craft the Drop messages manually and send them to Neutron or other supported chains to stake from it using ibc-hooks or the Skip API.
- (WIP) Use Drop Protocol through the Skip API.
There are various actions and queries that most Drop integrations consist of.
Actions:
- staking
- unstaking request
- unstaking withdrawal
Queries:
- instance contracts discovery
- dASSET denom
- dASSET balance
- exchange rate
- unstacking request status
Using TypeScript clients
If one is interested in direct interaction with Drop’s smart contracts on Neutron, you can use the generated TypeScript clients for smart contracts. They allow you to stake and unstake assets that are already on the Neutron chain.
All contract methods are covered in autogenerated code using https://github.com/neutron-org/contracts2ts tool. You can find examples of generated code in the https://github.com/hadronlabs-org/drop-contracts/tree/main/integration_tests/src/generated/contractLib repo.
This code is generated using https://github.com/hadronlabs-org/drop-contracts/blob/main/integration_tests/src/rebuild-client.ts configuration.
Crafting messages manually
Use the messages templates provided in this doc to send messages directly to the Drop’s smart contracts. This is especially useful in the case of issuing a transaction from another chain. For example, ATOM staking can be done in a single IBC-transfer that calls Drop’s contract via the ibc-hooks module on Neutron.
It must be noted that the approach described here results in dAssets minting on Neutron. It means that after staking with Drop, the user has to take the next action with dAssets from Neutron.
To simplify the work, one can use the SkipAPI. The process is as follows:
- https://api-docs.skip.money/reference/getassets endpoint allows getting the proper denom of a specific asset on a specific chain
- https://api-docs.skip.money/reference/getroutev2 endpoint allows building a proper route to transfer an asset to Neutron
- https://api-docs.skip.money/reference/getmsgsv2 endpoint allows building a message to be issued on the target chain to stake assets with Drop. To do that, one should specify the contract address and message needed in the
post_route_handlersection (COSMWASMCONTRACTMSGWRAPPER).
Here are the examples for staking transactions:
Staking ATOM from Cosmos Hub
Cosmos Hub transaction message:
{
"type": "cosmos-sdk/MsgTransfer",
"value": {
"memo": "{\"wasm\":{\"contract\":\"neutron15lp29w5vtntsgecwxp5qx0m7tka6km885pcls4e4ule6yz6dhvqstukrvu\",\"msg\":{\"bond\":{\"receiver\":\"neutron1vlw72wh7rvl2vg4w434a6h9mftsl5vfqzntwdh\"}}}}",
"receiver": "neutron15lp29w5vtntsgecwxp5qx0m7tka6km885pcls4e4ule6yz6dhvqstukrvu",
"sender": "cosmos1vlw72wh7rvl2vg4w434a6h9mftsl5vfqxvzvhs",
"source_channel": "channel-569",
"source_port": "transfer",
"timeout_height": {},
"timeout_timestamp": "1715100002194956544",
"token": {
"amount": "200000",
"denom": "uatom"
}
}
}
Cosmos Hub transaction memo:
{
"wasm": {
"contract": "neutron15lp29w5vtntsgecwxp5qx0m7tka6km885pcls4e4ule6yz6dhvqstukrvu",
"msg": {
"bond": {
"receiver": "neutron1vlw72wh7rvl2vg4w434a6h9mftsl5vfqzntwdh"
}
}
}
}
Staking ATOM from Osmosis
Osmosis transaction message:
{
"type": "cosmos-sdk/MsgTransfer",
"value": {
"memo": "{\"forward\":{\"channel\":\"channel-569\",\"next\":{\"wasm\":{\"contract\":\"neutron15lp29w5vtntsgecwxp5qx0m7tka6km885pcls4e4ule6yz6dhvqstukrvu\",\"msg\":{\"bond\":{\"receiver\":\"neutron1vlw72wh7rvl2vg4w434a6h9mftsl5vfqzntwdh\"}}}},\"port\":\"transfer\",\"receiver\":\"neutron15lp29w5vtntsgecwxp5qx0m7tka6km885pcls4e4ule6yz6dhvqstukrvu\",\"retries\":2,\"timeout\":1715100123336401403}}",
"receiver": "cosmos1vlw72wh7rvl2vg4w434a6h9mftsl5vfqxvzvhs",
"sender": "osmo1vlw72wh7rvl2vg4w434a6h9mftsl5vfqwh3upz",
"source_channel": "channel-0",
"source_port": "transfer",
"timeout_height": {},
"timeout_timestamp": "1715100123336399360",
"token": {
"amount": "99300",
"denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2"
}
}
}
Osmosis transaction memo:
{
"forward": {
"channel": "channel-569",
"next": {
"wasm": {
"contract": "neutron15lp29w5vtntsgecwxp5qx0m7tka6km885pcls4e4ule6yz6dhvqstukrvu",
"msg": {
"bond": {
"receiver": "neutron1vlw72wh7rvl2vg4w434a6h9mftsl5vfqzntwdh"
}
}
}
},
"port": "transfer",
"receiver": "neutron15lp29w5vtntsgecwxp5qx0m7tka6km885pcls4e4ule6yz6dhvqstukrvu",
"retries": 2,
"timeout": 1715100123336401400
}
}
As you can see, staking from Osmosis besides ibc-hook module requires using of pfm, s ATOM should be transferred to Cosmos Hub first and only after that to Neutron.
Direct usage via SkipAPI
The SkipAPI integration is a work in progress and will be available soon.
Instead of manually guiding SkipAPI on how to handle an asset (e.g., send to Neutron and stake), simply specify the desired result and let SkipAPI decide on the proper route to convert the assets. This approach is flexible but might have the potential caveat of the dAsset being bought on a DEX instead of being staked.
Queries
Instance contracts discovery
The factory contract returns all the other smart contracts for a specific instance with the following query:
{
"state": {}
}
Query result example:
{
"core_contract": "neutron1elxhch2kul3qk2whxawtfwe0l2ma0snec3fe6j4zp2wftwrhs33q2yzqwy",
"distribution_contract": "neutron1jnre6ef6mlcyfqsu8e9a2wf27farphsrgezlv0n8ezl9vfzdc7vqkmtvpm",
"puppeteer_contract": "neutron1zvk70rw9xk5nvkw3c8ms62n5v9t6zpayfkygn8m5pgwyrqzsjsaq2upegw",
"rewards_manager_contract": "neutron1t0spq2jeef7jvvydlaz4jxga37skrnqk0uuxa5d7nqcrjtnk79lsnfygj3",
"staker_contract": "neutron1qcz9uw5g3vwqmz7qcsmcua3vzps6amrsqrmf7r84xfe76g3ztsrqm82sfy",
"strategy_contract": "neutron1sjeppcavzvect0e5654tl4w06pp9sdx2pmxqy8m4jwv58jlwwv8quv86yy",
"token_contract": "neutron1wgtjgeprg53wthczzlzpg0vrytuvfl9j59lpf87qd3mtj2dq6xysxvwcxd",
"validators_set_contract": "neutron1sq87ct42mza6sjpj9qgkfk3ngyaqc03ykf78z3e5hez68zfs4a5qyxl5pg",
"withdrawal_manager_contract": "neutron1tgw3d229d93rhk3hv68ff7f8zjs9lgk9lv9pkvp8fmt46ja47u0s02xxhg",
"withdrawal_voucher_contract": "neutron1hvr35t4s0r9nrt4taujyu704r4wfynp4ed5dz32pa7xcwzgwr9vs7vu4gy"
}
Getting contracts with the TS client:
const wallet = await DirectSecp256k1HdWallet.fromMnemonic("Mnemonic", {
prefix: "neutron",
});
const client = await SigningCosmWasmClient.connectWithSigner(
"https://rpc-palvus.pion-1.ntrn.tech:443",
wallet,
{
gasPrice: GasPrice.fromString("0.025untrn"),
}
);
const factoryContractClient = new DropFactory.Client(
client,
"neutron15lp29w5vtntsgecwxp5qx0m7tka6km885pcls4e4ule6yz6dhvqstukrvu"
);
const res = await factoryContractClient.queryState();
console.log(res);
dASSET denom
The denom is stored in the token contract's config which can be obtained with the following query:
{
"config": {}
}
Query result example:
{
"core_address": "neutron1elxhch2kul3qk2whxawtfwe0l2ma0snec3fe6j4zp2wftwrhs33q2yzqwy",
"denom": "factory/neutron1wgtjgeprg53wthczzlzpg0vrytuvfl9j59lpf87qd3mtj2dq6xysxvwcxd/drop"
}
dASSET balance
Since dASSET is a native denom, you can get the dASSET balance by querying the bank module.
Exchange rate
Exchange rate tells you how many ASSET tokens correspond to a single dASSET token. Drop Protocol auto-compounds staking rewards, so this number is growing with time (it can possibly decrease if there are slashing events).
Exchange rate is queried from the core contract with the following query:
{
"exchange_rate": {}
}
The output is a float number represented as a string.
Example of using the client code:
const exchangeRate = parseFloat(await coreContractClient.queryExchangeRate());
Withdrawal NFT info
Withdrawal NFT represents the promise to return the user’s ASSET tokens once the unstaking period is over. The user receives an NFT once they provide a dASSET to unstake.
Here's the example on how to get the withdrawal NFT info from via generated client:
const vouchers = await withdrawalVoucherContractClient.queryTokens({
owner: neutronUserAddress,
});
Actions
Any action is represented as exchanging one token for another:
- Staking is an exchange of ASSET for dASSET.
- Unstaking request is an exchange of dASSET for withdrawal NFT.
- Unstaking withdrawal is an exchange of withdrawal NFT for ASSET.
NB: there is a planned plug-in integration which will allow you to skip the withdrawal step. The idea behind it is that the user can provide an additional gas token and lock their withdrawal NFT in the special contract that will process the withdrawal for them automatically. So, in the future it will be possible to unstake without the need to withdraw assets manually.
Staking
Staking with Drop is done by the following message to the core contract with assets to stake attached, it is possible to omit receiver field, in this case sender's address will be used:
{
"bond": {
"receiver": "neutron1receiveraddress"
}
}
For specifying the staking referral see Referral program integration.
Example of using the TS client:
const bondTxResult = await coreContractClient.bond(
neutronUserAddress,
{},
1.6,
undefined,
[
{
amount: "500000",
denom: neutronIBCDenom,
},
]
);
Staking of LSM shares is done with the very same message. The only difference is the asset attached: instead of being the native staking token, it should be the LSM share IBC-transferred to Neutron. For more details, see Onboarding from native staking.
Unstaking request
To make an unstaking request, a user should send the dASSET to the core contract with the following message:
{
"unbond": {}
}
Example of using the TS client:
let unBondTxResult = await coreContractClient.unbond(
neutronUserAddress,
1.6,
undefined,
[
{
amount: Math.floor(200_000 / exchangeRate).toString(),
denom: "token_factory_denom",
},
]
);
After that, the user receives a withdrawal voucher that they can use to withdraw their coins after the unbonding period.
Withdrawal
Withdrawal of the ASSET implies sending the NFT to withdrawal_manager with the following message attached:
{
"withdraw": {}
}
Example of using the TS client:
const voucherWithdrawTxResult = await voucherContractClient.sendNft(
neutronUserAddress,
{
token_id: tokenId,
contract: withdrawalManagerContractAddress,
msg: Buffer.from(
JSON.stringify({
withdraw: {},
})
).toString("base64"),
}
);