# What is CoinDrip?

The protocol for real-time ESDT payments

CoinDrip is the first token (ESDT) streaming protocol built on MultiversX. Instead of the regular token transfers, you can use CoinDrip to stream tokens each second toward a recipient.

If **you're a non-technical** person interested in using CoinDrip, check out the [FAQ](/faq)section.

If **you're a developer** building on MultiversX, check out the [Technical Guides](/technical-guides) and [Technical Reference](/technical-reference)to see how you can take advantage of the CoinDrip protocol in your dApp.&#x20;


# FAQ

This section will try to cover all relevant aspects of the CoinDrip protocol from a non-technical perspective.

{% hint style="info" %}
If you still have questions or issues that are not addressed here, email us at <contact@coindrip.finance> or [DM on Twitter](https://twitter.com/CoinDripHQ), and we'll be more than happy to help 😃
{% endhint %}


# Basics

Start streaming tokens in seconds...

## How can I access the CoinDrip protocol?

The easiest way of accessing the CoinDrip protocol is by using our [CoinDrip dApp](https://app.coindrip.finance/).

We're working with various projects from the MultiversX ecosystem to make the CoinDrip protocol accessible through more and more dApps. *If you're a project building on MultiversX and want to integrate the CoinDrip protocol, check out the* [Technical Guides](/technical-guides) *and* [Technical Reference](/technical-reference) *or contact us at <contact@coindrip.finance> or on* [*Twitter*](https://twitter.com/CoinDripHQ)*.*

## What is token streaming?

You define the number of tokens distributed in a specific time period. During that period, tokens unlock each second, and the recipient can claim them whenever he wants.

## How does token streaming work?

Let's say you want to send 2000 USDC to John from 1 Jan to 1 Feb. You'll create the stream with all these details. John's funds will start to unlock from 1 Jan each second. For example, on 15 Jan, John can claim half of his funds.

## How can I create a stream?

You need a MultiversX wallet, some EGLD, and/or an ESDT like USDC. Then, using any interface like the CoinDrip dApp, fill in the recipient, amount of tokens, and the duration, click the button and sign the transaction. That's it!

## Where are the tokens held?&#x20;

The tokens are locked inside our smart contract from the deposit until the recipient claims them. You can always use any MultiversX explorer to check on that.

## How can recipients access their tokens?

As tokens are streamed using the CoinDrip Protocol, the recipients can withdraw them at any time using the CoinDrip dApp or any other interface.

## Can I cancel a stream?

Yes, the sender and recipient can cancel a stream at any time if the stream was not marked as non-cancellable by the sender during creation. All funds are returned to the sender if the stream is canceled before the start time. If you cancel the stream after the start time but before the end time, the amount streamed so far is transferred to the recipient, and the remaining tokens return to your wallet. If the stream is canceled after the end time, all funds are transferred to the recipient.

## Can I modify a stream?

No, streams are not editable. After it was created, you can only cancel it based on the above conditions.


# Technical Glossary

## What is MultiversX?

[MultiversX ](https://multiversx.com/)is a highly scalable, secure, and decentralized blockchain network created to enable radically new applications for users, businesses, society, and the new metaverse frontier.

## What is a Smart Contract?

Smart contracts are programs stored on a blockchain that run when predetermined conditions are met. They are typically used to automate the execution of an agreement so that all participants can be immediately sure of the outcome without any intermediary's involvement or time loss.

## What is ESDT?

The MultiversX network natively supports the issuance of custom tokens without needing contracts such as ERC20 but addressing the same use cases. And due to the native in-protocol support, transactions with custom tokens do not require the VM. In effect, custom tokens are as fast and scalable as the native EGLD token itself.

## What is a token stream?

A real-time payment made up of seven properties:

* Sender - A MultiversX address
* Recipient - A MultiversX address
* Deposit - The number of streamed tokens
* Token - EGLD or ESDT Token
* Start time - The timestamp when the stream starts
* End time - The timestamp when the stream ends
* Is cancellable - Can the stream be canceled after it was created?


# Security

## Is the CoinDrip protocol safe?

The security of the CoinDrip protocol is our number one priority. Our team, accompanied by external auditors and consultants, has invested considerable effort to create a safe and reliable protocol. All contract code is publicly verifiable in our [GitHub repository](https://github.com/CoinDrip-finance/coindrip-protocol-sc), and Arda, a third-party auditor, [verified our contract](https://arda.run/audits/coindrip).

## Is the CoinDrip protocol transparent?

Transparency is one of our core principles. You can always check the smart contract on [Github](https://github.com/CoinDrip-finance/coindrip-protocol-sc) and  [MultiversX Explorer](https://explorer.multiversx.com/accounts/erd1qqqqqqqqqqqqqpgqqnm3x37972323nuv3l3kywev0n8q5n6gyc8qwljqz9).


# Known Issues

## Start Time

The blockchain must process a stream transaction before the start time of the stream. If the transaction is processed after the start time of the stream, the transaction will revert (fail).

{% hint style="warning" %}
This section is a work in progress, and relevant things will be added here in the next period.
{% endhint %}


# Technical Guides

This section aims to cover the technical aspects of the CoinDrip protocol and all the necessary information to integrate the CoinDrip protocol into your dApps:

* Smart Contract architecture and ABI
* The CoinDrip deployments on different chains

{% hint style="info" %}
If you need more technical details on the Smart Contract, consider checking the [Technical Reference](/technical-reference)section.
{% endhint %}

If you still have questions or issues that are not addressed here, email us at <contact@coindrip.finance>, and we'll be more than happy to help 😃


# Codebase

## Smart Contract code

The CoinDrip protocol is open-source, and the code is available on GitHub.

{% embed url="<https://github.com/CoinDrip-finance/coindrip-protocol-sc>" %}

The code is also audited by Arda, and you can check the audit report [here](https://arda.run/audits/coindrip).

## ABI

Depending on how you plan to integrate the CoinDrip protocol into your dApp, you might need CoinDrip's ABI (application binary interface).&#x20;

```json
{
    "buildInfo": {
        "rustc": {
            "version": "1.66.0-nightly",
            "commitHash": "b8c35ca26b191bb9a9ac669a4b3f4d3d52d97fb1",
            "commitDate": "2022-10-15",
            "channel": "Nightly",
            "short": "rustc 1.66.0-nightly (b8c35ca26 2022-10-15)"
        },
        "contractCrate": {
            "name": "coindrip",
            "version": "1.0.1-beta"
        },
        "framework": {
            "name": "multiversx-sc",
            "version": "0.39.2"
        }
    },
    "name": "CoinDrip",
    "constructor": {
        "inputs": [],
        "outputs": []
    },
    "endpoints": [
        {
            "name": "createStream",
            "mutability": "mutable",
            "payableInTokens": [
                "*"
            ],
            "inputs": [
                {
                    "name": "recipient",
                    "type": "Address"
                },
                {
                    "name": "start_time",
                    "type": "u64"
                },
                {
                    "name": "end_time",
                    "type": "u64"
                },
                {
                    "name": "_can_cancel",
                    "type": "optional<bool>",
                    "multi_arg": true
                }
            ],
            "outputs": []
        },
        {
            "docs": [
                "",
                "Calculates the recipient balance based on the amount stream so far and the already claimed amount",
                "|xxxx|*******|--|",
                "S            C  E",
                "S = start time",
                "xxxx = already claimed amount",
                "C = current time",
                "E = end time",
                "The zone marked with \"****...\" represents the recipient balance"
            ],
            "name": "recipientBalance",
            "mutability": "readonly",
            "inputs": [
                {
                    "name": "stream_id",
                    "type": "u64"
                }
            ],
            "outputs": [
                {
                    "type": "BigUint"
                }
            ]
        },
        {
            "docs": [
                "Calculates the sender balance based on the recipient balance and the claimed balance",
                "|----|-------|**|",
                "S   L.C      C  E",
                "S = start time",
                "L.C = last claimed amount",
                "C = current time",
                "E = end time",
                "The zone marked with \"**\" represents the sender balance"
            ],
            "name": "senderBalance",
            "mutability": "readonly",
            "inputs": [
                {
                    "name": "stream_id",
                    "type": "u64"
                }
            ],
            "outputs": [
                {
                    "type": "BigUint"
                }
            ]
        },
        {
            "docs": [
                "This endpoint can be used by the recipient of the stream to claim the stream amount of tokens"
            ],
            "name": "claimFromStream",
            "mutability": "mutable",
            "inputs": [
                {
                    "name": "stream_id",
                    "type": "u64"
                }
            ],
            "outputs": []
        },
        {
            "docs": [
                "This endpoint can be used the by sender or recipient of a stream to cancel the stream.",
                "!!! The stream needs to be cancelable (a property that is set when the stream is created by the sender)"
            ],
            "name": "cancelStream",
            "mutability": "mutable",
            "inputs": [
                {
                    "name": "stream_id",
                    "type": "u64"
                },
                {
                    "name": "_with_claim",
                    "type": "optional<bool>",
                    "multi_arg": true
                }
            ],
            "outputs": []
        },
        {
            "docs": [
                "After a stream was cancelled, you can call this endpoint to claim the streamed tokens as a recipient or the remaining tokens as a sender",
                "This endpoint is especially helpful when the recipient/sender is a non-payable smart contract",
                "For convenience, this endpoint is automatically called by default from the cancel_stream endpoint (is not instructed otherwise by the \"_with_claim\" param)"
            ],
            "name": "claimFromStreamAfterCancel",
            "mutability": "mutable",
            "inputs": [
                {
                    "name": "stream_id",
                    "type": "u64"
                }
            ],
            "outputs": []
        },
        {
            "name": "getStreamData",
            "mutability": "readonly",
            "inputs": [
                {
                    "name": "stream_id",
                    "type": "u64"
                }
            ],
            "outputs": [
                {
                    "type": "Stream"
                }
            ]
        },
        {
            "name": "getStreamListByAddress",
            "mutability": "readonly",
            "inputs": [
                {
                    "name": "address",
                    "type": "Address"
                }
            ],
            "outputs": [
                {
                    "type": "variadic<u64>",
                    "multi_result": true
                }
            ]
        },
        {
            "name": "getLastStreamId",
            "mutability": "readonly",
            "inputs": [],
            "outputs": [
                {
                    "type": "u64"
                }
            ]
        }
    ],
    "events": [
        {
            "identifier": "createStream",
            "inputs": [
                {
                    "name": "stream_id",
                    "type": "u64",
                    "indexed": true
                },
                {
                    "name": "sender",
                    "type": "Address",
                    "indexed": true
                },
                {
                    "name": "recipient",
                    "type": "Address",
                    "indexed": true
                },
                {
                    "name": "payment_token",
                    "type": "EgldOrEsdtTokenIdentifier",
                    "indexed": true
                },
                {
                    "name": "payment_nonce",
                    "type": "u64",
                    "indexed": true
                },
                {
                    "name": "deposit",
                    "type": "BigUint",
                    "indexed": true
                },
                {
                    "name": "start_time",
                    "type": "u64",
                    "indexed": true
                },
                {
                    "name": "end_time",
                    "type": "u64",
                    "indexed": true
                }
            ]
        },
        {
            "identifier": "claimFromStream",
            "inputs": [
                {
                    "name": "stream_id",
                    "type": "u64",
                    "indexed": true
                },
                {
                    "name": "amount",
                    "type": "BigUint",
                    "indexed": true
                },
                {
                    "name": "finalized",
                    "type": "bool",
                    "indexed": true
                }
            ]
        },
        {
            "identifier": "cancelStream",
            "inputs": [
                {
                    "name": "stream_id",
                    "type": "u64",
                    "indexed": true
                },
                {
                    "name": "canceled_by",
                    "type": "Address",
                    "indexed": true
                },
                {
                    "name": "claimed_amount",
                    "type": "BigUint",
                    "indexed": true
                }
            ]
        }
    ],
    "hasCallback": false,
    "types": {
        "BalancesAfterCancel": {
            "type": "struct",
            "fields": [
                {
                    "name": "sender_balance",
                    "type": "BigUint"
                },
                {
                    "name": "recipient_balance",
                    "type": "BigUint"
                }
            ]
        },
        "Stream": {
            "type": "struct",
            "fields": [
                {
                    "name": "sender",
                    "type": "Address"
                },
                {
                    "name": "recipient",
                    "type": "Address"
                },
                {
                    "name": "payment_token",
                    "type": "EgldOrEsdtTokenIdentifier"
                },
                {
                    "name": "payment_nonce",
                    "type": "u64"
                },
                {
                    "name": "deposit",
                    "type": "BigUint"
                },
                {
                    "name": "claimed_amount",
                    "type": "BigUint"
                },
                {
                    "name": "can_cancel",
                    "type": "bool"
                },
                {
                    "name": "start_time",
                    "type": "u64"
                },
                {
                    "name": "end_time",
                    "type": "u64"
                },
                {
                    "name": "balances_after_cancel",
                    "type": "Option<BalancesAfterCancel>"
                }
            ]
        }
    }
}
```

You can also obtain the ABI JSON if you build the contract using [mxpy](https://docs.multiversx.com/sdk-and-tools/sdk-py/installing-mxpy/).

```bash
git clone https://github.com/CoinDrip-finance/coindrip-protocol-sc
cd ./coindrip-protocol-sc
mxpy contract build
```


# Chains

Currently, CoinDrip has two official deployments on MultiversX mainnet and devnet.

{% tabs %}
{% tab title="Mainnet" %}
Contract Address: [erd1qqqqqqqqqqqqqpgqqnm3x37972323nuv3l3kywev0n8q5n6gyc8qwljqz9](https://explorer.multiversx.com/accounts/erd1qqqqqqqqqqqqqpgqqnm3x37972323nuv3l3kywev0n8q5n6gyc8qwljqz9)
{% endtab %}

{% tab title="Devnet" %}
Contract Address: [erd1qqqqqqqqqqqqqpgqfgned8q9zqwaeya4sc0stf7elpj6ylsdlpzqwhk5ye](https://devnet-explorer.multiversx.com/accounts/erd1qqqqqqqqqqqqqpgqfgned8q9zqwaeya4sc0stf7elpj6ylsdlpzqwhk5ye)

*If you want to use CoinDrip on Devnet, you will need some xEGLD and a token to stream. You can use the* [*web wallet*](https://devnet-wallet.multiversx.com/unlock/) *for the xEGLD faucet and this* [*ESDT faucet*](https://r3d4.fr/faucet)*.*&#x20;
{% endtab %}
{% endtabs %}

{% hint style="info" %}
The official deployments are made by the CoinDrip team and used in the official CoinDrip dApp.
{% endhint %}


# API

TODO


# Technical Reference

This section aims to provide further technical information related to the CoinDrip Protocol smart contract.


# Streams

Streams are the core component of the CoinDrip protocol. All things that are built and that will be built will be around token streams for real-time payments.

The actual Stream struct stored on the blockchain looks like this:

```rust
struct Stream<M: ManagedTypeApi> {
    pub sender: ManagedAddress<M>,
    pub recipient: ManagedAddress<M>,
    pub payment_token: EgldOrEsdtTokenIdentifier<M>,
    pub payment_nonce: u64,
    pub deposit: BigUint<M>,
    pub claimed_amount: BigUint<M>,
    pub can_cancel: bool,
    pub start_time: u64,
    pub end_time: u64,
    pub balances_after_cancel: Option<BalancesAfterCancel<M>>
}

struct BalancesAfterCancel<M: ManagedTypeApi> {
    pub sender_balance: BigUint<M>,
    pub recipient_balance: BigUint<M>
}
```

You can find more information in the [Codebase](/technical-guides/codebase)section.


# Endpoints

## Create Stream

The create stream endpoint requires the sender to transfer the streamed token to the CoinDrip smart contract. If the transaction is successful, the stream is registered on the blockchain. As soon as the start time is reached, the CoinDrip protocol will unlock tokens for the recipient.

```rust
#[payable("*")]
#[endpoint(createStream)]
fn create_stream(
    &self,
    recipient: ManagedAddress,
    start_time: u64,
    end_time: u64,
    can_cancel: OptionalValue<bool>
) 
```

{% hint style="info" %}
You must also send the token you want to stream in the create stream transaction.
{% endhint %}

{% hint style="danger" %}
The start\_time must be bigger than `self.blockchain().get_block_timestamp(),` or the transaction will fail.
{% endhint %}

Example of building a create stream transaction payload using [mx js sdk](https://github.com/multiversx/mx-sdk-js-core):

```typescript
TransactionPayload.contractCall()
      .setFunction(new ContractFunction("ESDTTransfer"))
      .addArg(new BytesValue(Buffer.from('USDC-a2sd58', "utf-8"))) // streamed token identifier
      .addArg(new BigUIntValue(TokenPayment.egldFromAmount('100').valueOf())) // streamed token amount
      .addArg(new BytesValue(Buffer.from("createStream", "utf-8")))
      .addArg(new AddressValue(new Address('erd1aaaa.....'))) // recipient address
      .addArg(new U64Value(1674158572)) // start time
      .addArg(new U64Value(1674159572)) // end time
      .build();
```

## Claim from stream

This endpoint transfers the amount of already streamed tokens from the CoinDrip smart contract to the recipient's wallet.&#x20;

```rust
#[endpoint(claimFromStream)]
fn claim_from_stream(
    &self,
    stream_id: u64
)
```

{% hint style="info" %}
The recipient of the stream can only call this endpoint after the present time went over the start time of the stream.
{% endhint %}

Example of building a claim from stream transaction payload using [mx js sdk](https://github.com/multiversx/mx-sdk-js-core):

```typescript
TransactionPayload.contractCall()
      .setFunction(new ContractFunction("claimFromStream"))
      .addArg(new U64Value(12)) // stream id
      .build();
```

## Cancel stream

This endpoint can be called by the sender or the recipient and will cancel a stream at any time **if the stream was not marked as non-cancellable by the sender during creation**. If the stream is canceled before the start time, all funds are returned to the sender. If you cancel the stream after the start time, but before the end time, the amount that was streamed so far is transferred to the recipient, and the remaining tokens come back to your wallet. If the stream is canceled after the end time, all funds are transferred to the recipient.

```rust
#[endpoint(cancelStream)]
fn cancel_stream(
    &self,
    stream_id: u64,
    with_claim: OptionalValue<bool>
)
```

{% hint style="info" %}
If you call this endpoint with the "with\_claim" param "true", it will also claim your remaining funds (if any) after the cancellation.&#x20;
{% endhint %}

Example of building a cancel stream transaction payload using [mx js sdk](https://github.com/multiversx/mx-sdk-js-core):

```typescript
TransactionPayload.contractCall()
      .setFunction(new ContractFunction("cancelStream"))
      .addArg(new U64Value(9)) // stream id
      .build();
```

## Claim from stream after cancel

After a stream is canceled, you can call this endpoint to claim the streamed tokens as a recipient or the remaining tokens as a sender (if any). **This endpoint is especially helpful when the recipient/sender is a non-payable smart contract.**

For convenience, this endpoint is automatically called by default from the [#cancel-stream](#cancel-stream "mention") endpoint (is not instructed otherwise by the "with\_claim" param).

```rust
#[endpoint(claimFromStreamAfterCancel)]
fn claim_from_stream_after_cancel(
        &self,
        stream_id: u64
)
```

Example of building a claim from steram after cancel transaction payload using [mx js sdk](https://github.com/multiversx/mx-sdk-js-core):

```typescript
TransactionPayload.contractCall()
      .setFunction(new ContractFunction("claimFromStreamAfterCancel"))
      .addArg(new U64Value(9)) // stream id
      .build();
```


# Views

## recipientBalance

This view is used to return the active balance of the recipient of a specific stream.

```rust
#[view(recipientBalance)]
fn recipient_balance(&self, stream_id: u64) -> BigUint
```

You can use the MultiversX public APIs to query the CoinDrip smart contract. Let's use axios for an example of such a call:

```typescript
await axios.post('https://devnet-gateway.multiversx.com/vm-values/int',
    {
        funcName: 'recipientBalance',
        scAddress: 'erd1qqqqqqqqqqqqqpgqfgned8q9zqwaeya4sc0stf7elpj6ylsdlpzqwhk5ye',
        args: ["STREAM ID HEX"],
        value: "0"
    }
);
```

## senderBalance

This view is used to return the active balance of the sender of a specific stream.

```rust
#[view(senderBalance)]
fn sender_balance(&self, stream_id: u64) -> BigUint
```

You can use the MultiversX public APIs to query the CoinDrip smart contract. Let's use axios for an example of such a call:

```typescript
await axios.post('https://devnet-gateway.multiversx.com/vm-values/int',
    {
        funcName: 'senderBalance',
        scAddress: 'erd1qqqqqqqqqqqqqpgqfgned8q9zqwaeya4sc0stf7elpj6ylsdlpzqwhk5ye',
        args: ["STREAM ID HEX"],
        value: "0"
    }
);
```

## getStreamData

This view will return a Stream struct based on the stream id.

```rust
#[view(getStreamData)]
fn get_stream(&self, stream_id: u64) -> Stream<Self::Api>
```

Because this will return a struct, we'll present you another way to query the smart contract and decode the struct using [mx js sdk](https://github.com/multiversx/mx-sdk-js-core):

```typescript
const getStreamDetails = async (streamId: number): Promise<any> => {
    let abi = new SmartContractAbi(AbiRegistry.create(ScAbi), ["CoinDrip"]);
    let contract = new SmartContract({ address: new Address(contractAddress), abi: abi });
  
    let getSteramDetails = <Interaction>contract.methods.getStreamData([streamId]);
  
    const parser = new ResultsParser();
  
    let ViewQueryResponse = await new ApiNetworkProvider(network.apiAddress).queryContract(
      getSteramDetails.buildQuery()
    );
    let ViewEndpointDefinition = contract.getEndpoint("getStreamData");
  
    let ViewValues = parser?.parseQueryResponse(ViewQueryResponse, ViewEndpointDefinition)?.firstValue?.valueOf();
  
    return ViewValues;
};
```


