Skip to content
This repository has been archived by the owner on Feb 23, 2023. It is now read-only.

Replace pseudocode CAP-33 examples with real SDK code. #254

Merged
merged 6 commits into from
Oct 15, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
281 changes: 249 additions & 32 deletions content/docs/glossary/sponsored-reserves.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ title: Sponsored Reserves
order:
---

import { CodeExample } from "components/CodeExample";

Protocol 14 introduces operations that allow an account to pay the base reserves for another account. This is done by using the [Begin Sponsoring Future Reserves](../start/list-of-operations.mdx#begin-sponsoring-future-reserves) and [End Sponsoring Future Reserves](../start/list-of-operations.mdx#end-sponsoring-future-reserves) operations.

The sponsoring account establishes the is-sponsoring-future-reserves-for relationship, and the sponsored account terminates it. While this relationship exists, reserve requirements that would normally accumulate on the sponsored account will now accumulate on the sponsoring account. Both operations must appear in a single transaction, which guarantees that both the sponsoring and sponsored accounts agree to every sponsorship.
Expand Down Expand Up @@ -33,35 +35,6 @@ Anything that increases the minimum balance can be sponsored (Accounts, Offers,

At the end of any transaction, there must be no ongoing is-sponsoring-future-reserves-for relationships. This is why these two operations must be used together in a single transaction.

### Simple Example

In the example below, `A` is sponsoring a trustline for `B`. Notice how the `CHANGE_TRUST` operation is sandwiched between the begin and end sponsoring operations.

```
sourceAccount: B
operations[0]:
sourceAccount: A
body:
type: BEGIN_SPONSORING_FUTURE_RESERVES
sponsoredID: B
operations[1]:
sourceAccount: B
body:
type: CHANGE_TRUST
line: X
limit: INT64_MAX
operations[2]:
sourceAccount: B
body:
type: END_SPONSORING_FUTURE_RESERVES
```

#### Other Examples

https://github.com/stellar/stellar-protocol/blob/master/core/cap-0033.md#example-sponsoring-account-creation

https://github.com/stellar/stellar-protocol/blob/master/core/cap-0033.md#example-two-trust-lines-with-different-sponsors

### Revoke Sponsorship

[Revoke Sponsorship](../start/list-of-operations.mdx#revoke-sponsorship) is the third and final operation relevant to sponsorships. It allows the sponsoring account to remove/transfer sponsorships of existing ledgerEntries and signers. If the ledgerEntry/signer is not sponsored, the owner of the ledgerEntry/signer can establish a sponsorship if it is the beneficiary of a is-sponsoring-future-reserves-for relationship.
Expand All @@ -85,8 +58,252 @@ See [Revoke Sponsorship](../start/list-of-operations.mdx#revoke-sponsorship) for

The logic above does not detail any of the error cases, which are specified [here](../start/list-of-operations.mdx#revoke-sponsorship).

#### Examples
## Examples
Each example builds on itself, referencing variables from previous snippets. We'll demonstrate a few different things you can do with sponsoring:
- Sponsor creation of a trustline for another account.
- Sponsor **two** trustlines for an account via two *different* sponsors.
- Transfer sponsorship responsibility from one account to another.
- Revoke sponsorship by an account entirely.

(For brevity, we'll assume the existence of a `SignAndSend(...)` method (defined [below](#footnote)) which will creates and submits a transaction with the proper parameters and error-checking.

### Preamble
We'll start by including the boilerplate of account and asset creation.

<CodeExample>

```go
package main

import (
"fmt"
"net/http"

sdk "github.com/stellar/go/clients/horizonclient"
"github.com/stellar/go/keypair"
"github.com/stellar/go/network"
protocol "github.com/stellar/go/protocols/horizon"
"github.com/stellar/go/txnbuild"
)

func main() {
client := sdk.DefaultTestNetClient

// Both S1 and S2 will be sponsors for A at various points in time.
S1, A, S2 := keypair.MustRandom(), keypair.MustRandom(), keypair.MustRandom()
addressA := A.Address()

for _, pair := range []*keypair.Full{S1, A, S2} {
resp, err := http.Get("https://friendbot.stellar.org/?addr=" + pair.Address())
check(err)
resp.Body.Close()
fmt.Println("Funded", pair.Address())
}

// Load the corresponding account for both A and C.
s1Account, err := client.AccountDetail(sdk.AccountRequest{AccountID: S1.Address()})
check(err)
aAccount, err := client.AccountDetail(sdk.AccountRequest{AccountID: addressA})
check(err)
s2Account, err := client.AccountDetail(sdk.AccountRequest{AccountID: S2.Address()})
check(err)

// Arbitrary assets to sponsor trustlines for. Let's assume they make sense.
assets := []txnbuild.CreditAsset{
txnbuild.CreditAsset{Code: "ABCD", Issuer: S1.Address()},
txnbuild.CreditAsset{Code: "EFGH", Issuer: S1.Address()},
txnbuild.CreditAsset{Code: "IJKL", Issuer: S2.Address()},
}

// ...
```

</CodeExample>


### Sponsoring a Trustline
Now, let's sponsor trustlines for Account A. Notice how the `CHANGE_TRUST` operation is sandwiched between the begin and end sponsoring operations and that all relevant accounts need to sign the transaction.

<CodeExample>

```go
//
// 1. S1 will sponsor a trustline for Account A.
//
sponsorTrustline := []txnbuild.Operation{
&txnbuild.BeginSponsoringFutureReserves{
SourceAccount: &s1Account,
SponsoredID: addressA,
},
&txnbuild.ChangeTrust{
SourceAccount: &aAccount,
Line: &assets[0],
Limit: txnbuild.MaxTrustlineLimit,
},
&txnbuild.EndSponsoringFutureReserves{},
}

// Note that while A can submit this transaction, both sign it.
SignAndSend(client, &aAccount, []*keypair.Full{S1, A}, sponsorTrustline...)
fmt.Println("Sponsored a trustline of", A.Address())

//
// 2. Both S1 and S2 sponsor trustlines for Account A for different assets.
//
sponsorTrustline = []txnbuild.Operation{
&txnbuild.BeginSponsoringFutureReserves{
SourceAccount: &s1Account,
SponsoredID: addressA,
},
&txnbuild.ChangeTrust{
Line: &assets[1],
Limit: txnbuild.MaxTrustlineLimit,
},
&txnbuild.EndSponsoringFutureReserves{},

https://github.com/stellar/stellar-protocol/blob/master/core/cap-0033.md#example-revoke-sponsorship
&txnbuild.BeginSponsoringFutureReserves{
SourceAccount: &s2Account,
SponsoredID: addressA,
},
&txnbuild.ChangeTrust{
Line: &assets[2],
Limit: txnbuild.MaxTrustlineLimit,
},
&txnbuild.EndSponsoringFutureReserves{},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the transaction source account is aAccount, so the source account here doesn't have to be specified. I noticed in some of your other examples you do specify the source account even if it's equal to the transaction source account. Might be a good idea to keep the usage consistent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, I'll consolidate this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed, I believe

}

// Note that all 3 accounts must approve/sign this transaction.
SignAndSend(client, &aAccount, []*keypair.Full{S1, S2, A}, sponsorTrustline...)
fmt.Println("Sponsored two trustlines of", A.Address())
```

</CodeExample>


### Transferring Sponsorship
Suppose that now Signer 1 wants to transfer responsibility of sponsoring reserves for the trustline to Sponsor 2. This is accomplished by sandwiching the transfer between the `BEGIN/END_SPONSORING_FUTURE_RESERVES` operations. Both of the participants must sign the transaction, though either can submit it.

<CodeExample>

```go
//
// 3. Transfer sponsorship of B's second trustline from S1 to S2.
//
transferOps := []txnbuild.Operation{
&txnbuild.BeginSponsoringFutureReserves{
SourceAccount: &s2Account,
SponsoredID: S1.Address(),
},
&txnbuild.RevokeSponsorship{
SourceAccount: &s1Account,
SponsorshipType: txnbuild.RevokeSponsorshipTypeTrustLine,
Account: &addressA,
TrustLine: &txnbuild.TrustLineID{
Account: addressA,
Asset: assets[1],
},
},
&txnbuild.EndSponsoringFutureReserves{},
}

// Notice that while the old sponsor *sends* the transaction, both sponsors
// must *approve* the transfer.
SignAndSend(client, &s1Account, []*keypair.Full{S1, S2}, transferOps...)
fmt.Println("Transferred sponsorship for", A.Address())
```

</CodeExample>

At this point, Signer 1 is only sponsoring the first asset (arbitrarily coded as `ABCD`), while Signer 2 is sponsoring the other two assets.

### Sponsorship Revocation
Finally, we can demonstrate complete revocation of sponsorships. Below, Signer 2 removes themselves from all responsibility over the two asset trustlines. Notice that Account A is not involved at all, since revocation should be performable purely at the sponsor's discretion.

<CodeExample>

```go
//
// 4. S2 revokes sponsorship of B's trustlines entirely.
//
revokeOps := []txnbuild.Operation{
&txnbuild.RevokeSponsorship{
SponsorshipType: txnbuild.RevokeSponsorshipTypeTrustLine,
Account: &addressA,
TrustLine: &txnbuild.TrustLineID{
Account: addressA,
Asset: assets[1],
},
},
&txnbuild.RevokeSponsorship{
SponsorshipType: txnbuild.RevokeSponsorshipTypeTrustLine,
Account: &addressA,
TrustLine: &txnbuild.TrustLineID{
Account: addressA,
Asset: assets[2],
},
},
}

SignAndSend(client, &s2Account, []*keypair.Full{S2}, revokeOps...)
fmt.Println("Revoked sponsorship for", A.Address())
} // ends main()
```

</CodeExample>


### Other Examples
If you'd like other examples, or want to view a more-generic pseudocode breakdown of these sponsorship scenarios, you can refer to [CAP-0033](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0033.md#example-revoke-sponsorship) directly.


### Footnote
An implementation of `SignAndSend` might look something like this:

<CodeExample>

```go
// Builds a transaction containing `operations...`, signed (by `signers`), and
// submitted using the given `client` on behalf of `account`.
func SignAndSend(
client *sdk.Client,
account txnbuild.Account,
signers []*keypair.Full,
operations ...txnbuild.Operation,
) protocol.Transaction {
// Build, sign, and submit the transaction
tx, err := txnbuild.NewTransaction(
txnbuild.TransactionParams{
SourceAccount: account,
IncrementSequenceNum: true,
BaseFee: txnbuild.MinBaseFee,
Timebounds: txnbuild.NewInfiniteTimeout(),
Operations: operations,
},
)
check(err)

for _, signer := range signers {
tx, err = tx.Sign(network.TestNetworkPassphrase, signer)
check(err)
}

txResp, err := client.SubmitTransaction(tx)
if err != nil {
if prob := sdk.GetError(err); prob != nil {
fmt.Printf(" problem: %s\n", prob.Problem.Detail)
fmt.Printf(" extras: %s\n", prob.Problem.Extras["result_codes"])
}
check(err)
}

return txResp
}

func check(err error) {
if err != nil {
panic(err)
}
}
```

https://github.com/stellar/stellar-protocol/blob/master/core/cap-0033.md#example-transfer-sponsorship
</CodeExample>