Skip to main content

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:

  1. Use the auto-generated TypeScript client for smart contracts to interact with Drop Protocol.
  2. 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.
  3. (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:

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"),
}
);