# Rewards

After a successful referral, Spindl will automatically credit wallets with rewards in an on-chain smart contract. The smart contract provides methods to directly to check the status of rewards for a particular wallet and facilitate a withdrawal transaction on-chain.

{% hint style="info" %}
The Smart Contract address and Campaign ID to use in the methods below are specific to your running campaigns, and available within the Spindl Dashboard.

To test, you can use the [0xEC16738B5c89837e90f117dEAb1e9743Be562d69](https://goerli.arbiscan.io/address/0xEC16738B5c89837e90f117dEAb1e9743Be562d69) on Arbitrum Goerli Test Net, with Campaign ID `1`(details [below](#test-contract)).
{% endhint %}

{% hint style="info" %}
All methods below are Smart Contract calls, that can be called via a library like Ethers.js
{% endhint %}

### Check Available Rewards

**RPC Call**

```solidity
getRecipient(_campaignId, _recpientAddress) public view returns (RecipientDetails memory) 
```

([Etherscan Reference](https://goerli.arbiscan.io/address/0xEC16738B5c89837e90f117dEAb1e9743Be562d69#readProxyContract#F3))

**Parameters**

* *Campaign ID*: This is the campaign id associated with the campaign you are running. If you are running multiple campaigns, you can check for rewards for each one individually.
* *Recipient Address*: Logged in wallet.

**Response**

```solidity
struct RecipientDetails {
    RecipientStatus status;
    uint256 earned;
    uint256 withdrawn;
}

enum RecipientStatus {
    NONE,
    ACTIVE,
    PAUSED
}
```

(*Note*: The status will almost always be NONE or ACTIVE. The PAUSED status is a special flag that only you can trigger, for suspicious referrers)

**Examples**

<details>

<summary>Wallet Has Pending Rewards</summary>

Recipient 0x0000000000000000000000000000000000000021 has been credited some rewards. The return value is:

```
[1,1200000000,0]
```

The status is ACTIVE (1), which means the recipient is eligible to withdraw funds. They have earned `1200000000` wei, and withdrawn `0` wei, so they are eligible to withdraw `1200000000 - 0 = 1200000000`

</details>

<details>

<summary>Wallet Has No Rewards</summary>

Recipient 0x0000000000000000000000000000000000000000 has never been rewarded. The return value is:

`[0, 0, 0`]

The status is NONE (0), which means the recipient address has never been credited. The earned and withdrawn values are 0 (since there is nothing owed to them).

</details>

<details>

<summary>Wallet Has Withdrawn All Rewards</summary>

Recipient 0xD4ed4369ae2c27924ee59bd49459C642C20395B2 has withdrawn all rewards. In this case, the return value is:

`[0,1200000000,1200000000]`

The status is ACTIVE (1), which means the recipient is eligible to withdraw funds. They have earned `1200000000` wei, and withdrawn `1200000000` wei, so there are no remaining funds left to withdraw.

</details>

### Withdraw Rewards

**Transaction Call**

```solidity
withdrawRecipientEarnings(uint32 _campaignId, uint256 _amount, address _to) public virtual
```

([Etherscan Reference](https://goerli.arbiscan.io/address/0xEC16738B5c89837e90f117dEAb1e9743Be562d69#writeProxyContract#F15))

**Parameters**

* *Campaign ID*: This is the campaign id associated with the campaign you are running.
* *Amount*: The amount to withdraw. This is usually the total withdrawal amount, but can be a smaller amount as well. This will be in the currency for the campaign.&#x20;
* *To*: The recipient of the funds. This is usually the address of the wallet calling this function, but you can set it to another address specific by the user

**Returns**

This is a transaction, so there will be no return type. The transaction should be successful if the caller is owed at least that amount in the campaign.

### Campaign Details

**RPC Call**

```solidity
campaigns(_campaignId) public view returns (totalBudget uint256, status uint8, paymentType address, totalEarned uint256)
```

([Etherscan Reference](https://goerli.arbiscan.io/address/0xEC16738B5c89837e90f117dEAb1e9743Be562d69#readProxyContract#F2))

#### Parameters

* *Campaign ID*: This is the campaign id associated with the campaign you are running.

**Returns**

* Total Budget: The total budget set by the owner of the campaign
* Status
* Payment Type: The ERC20 token, or 0x0... for the native currency&#x20;
* Total Earned: The total amount of token allocated referrers and referees.

### Test Contract

Here is an example contract on the Arbitrum Tesnet:

* Address: 0xEC16738B5c89837e90f117dEAb1e9743Be562d69 ([URL](https://goerli.arbiscan.io/address/0xEC16738B5c89837e90f117dEAb1e9743Be562d69#readProxyContract))
* Campaign ID: 1

We populated the campaign with earnings for these referrer addresses to make testing easier:

```typescript
0xD4ed4369ae2c27924ee59bd49459C642C20395B2
0x0000000000000000000000000000000000000021
0x0000000000000000000000000000000000000022
0x0000000000000000000000000000000000000023
0x0000000000000000000000000000000000000024
0x0000000000000000000000000000000000000025
0x0000000000000000000000000000000000000026
0x0000000000000000000000000000000000000027
0x0000000000000000000000000000000000000028
0x0000000000000000000000000000000000000029
0x0000000000000000000000000000000000000030
```

### Contract ABI

You can use this to interact with the contract with any Web3 client libraries, like Ethers.js

```
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BadAddress","type":"error"},{"inputs":[],"name":"BadPayment","type":"error"},{"inputs":[],"name":"BadRequest","type":"error"},{"inputs":[],"name":"ImproperCampaignState","type":"error"},{"inputs":[],"name":"IncorrectAmount","type":"error"},{"inputs":[],"name":"OverAttributed","type":"error"},{"inputs":[],"name":"PaymentTypeDoesNotExist","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"campaignId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"withdrawAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalBalance","type":"uint256"}],"name":"BalanceWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"campaignId","type":"uint32"},{"indexed":false,"internalType":"address","name":"paymentType","type":"address"}],"name":"CampaignCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"campaignId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"updatedBudget","type":"uint256"}],"name":"CampaignFunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"campaignId","type":"uint32"},{"indexed":false,"internalType":"enum PayoutVault.CampaignStatus","name":"oldState","type":"uint8"},{"indexed":false,"internalType":"enum PayoutVault.CampaignStatus","name":"newState","type":"uint8"}],"name":"CampaignStatusUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"ManagerAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"paymentAddress","type":"address"}],"name":"PaymentAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"campaignId","type":"uint32"},{"indexed":false,"internalType":"address","name":"recipientAddress","type":"address"}],"name":"RecipientAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"campaignId","type":"uint32"},{"indexed":true,"internalType":"address","name":"addr","type":"address"},{"indexed":false,"internalType":"enum PayoutVault.RecipientStatus","name":"status","type":"uint8"}],"name":"RecipientStatusUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"campaignId","type":"uint32"},{"indexed":true,"internalType":"address","name":"recipientAddress","type":"address"},{"indexed":false,"internalType":"address","name":"paymentType","type":"address"},{"indexed":false,"internalType":"uint256","name":"newEarnings","type":"uint256"}],"name":"UpdateCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"campaignId","type":"uint32"},{"indexed":true,"internalType":"address","name":"recipientAddress","type":"address"},{"indexed":false,"internalType":"address","name":"paymentType","type":"address"},{"indexed":false,"internalType":"uint256","name":"totalWithdrawn","type":"uint256"},{"indexed":false,"internalType":"enum PayoutVault.RecipientWithdrawType","name":"withdrawType","type":"uint8"}],"name":"WithdrawSuccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"WorkerAddressUpdated","type":"event"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"addERC20Payment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_campaignId","type":"uint32"},{"internalType":"address[]","name":"_recipientAddresses","type":"address[]"}],"name":"addRecipients","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"campaignCounter","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"campaigns","outputs":[{"internalType":"uint256","name":"totalBudget","type":"uint256"},{"internalType":"enum PayoutVault.CampaignStatus","name":"status","type":"uint8"},{"internalType":"address","name":"paymentType","type":"address"},{"internalType":"uint256","name":"totalEarned","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_paymentType","type":"address"},{"internalType":"address[]","name":"_recipientAddresses","type":"address[]"}],"name":"createCampaign","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_campaignId","type":"uint32"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"fundCampaign","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_campaignId","type":"uint32"},{"internalType":"address","name":"_recipientAddress","type":"address"}],"name":"getRecipient","outputs":[{"components":[{"internalType":"enum PayoutVault.RecipientStatus","name":"status","type":"uint8"},{"internalType":"uint256","name":"earned","type":"uint256"},{"internalType":"uint256","name":"withdrawn","type":"uint256"}],"internalType":"struct PayoutVault.RecipientDetails","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_managerAddress","type":"address"},{"internalType":"address","name":"_workerAddress","type":"address"},{"internalType":"address[]","name":"_paymentTypes","type":"address[]"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"managerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"paymentTypes","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_campaignId","type":"uint32"},{"internalType":"address[]","name":"_recipientAddresses","type":"address[]"}],"name":"pushRecipientEarnings","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_campaignId","type":"uint32"},{"internalType":"enum PayoutVault.CampaignStatus","name":"_status","type":"uint8"}],"name":"setCampaignStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newAddress","type":"address"}],"name":"setManagerAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_campaignId","type":"uint32"},{"internalType":"address","name":"_recipientAddress","type":"address"},{"internalType":"enum PayoutVault.RecipientStatus","name":"_status","type":"uint8"}],"name":"setRecipientStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newAddress","type":"address"}],"name":"setWorkerAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"performData","type":"bytes"}],"name":"updateBalances","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"campaignId","type":"uint32"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct PayoutVault.RecipientEarnings[]","name":"_campaigns","type":"tuple[]"},{"internalType":"address","name":"_to","type":"address"}],"name":"withdrawManyRecipientEarnings","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_campaignId","type":"uint32"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"withdrawRecipientEarnings","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_campaignId","type":"uint32"},{"internalType":"address","name":"_to","type":"address"}],"name":"withdrawRemainingCampaignBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"workerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.spindl.xyz/spindl/techncial/on-chain/rewards.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
