Skip to content
This repository has been archived by the owner on Nov 26, 2024. It is now read-only.

Commit

Permalink
Demo 5: Transfer hooks (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
urani-engineering-helper authored Mar 29, 2024
1 parent d72779f commit ae5740c
Show file tree
Hide file tree
Showing 36 changed files with 9,204 additions and 14 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Compared to older platforms like Bitcoin and EVM-based protocols, Solana is:
* **[Demo 2. Anchor and CPI](demos/backend/02_anchor_cpi)**
* **[Demo 3. Program Derived Addresses](demos/backend/03_anchor_pda)**
* **[Demo 4: PDA and CPI on Anchor](demos/backend/04_pda_and_cpi)**

* **[Demo 5: Transfer Hooks](demos/backend/05_transfer_hooks)**

<br>

Expand Down
2 changes: 1 addition & 1 deletion chapters/02_dev_env.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

4. Compile the program (down to [Berkley Packet Filter](https://solana.com/docs/programs/faq#berkeley-packet-filter-bpf) byte-code that will then be deployed to the blockchain).

5. Generate the program's public address (a new unique keypair, on which the pubkey is the `programId`).
5. Generate the program's public address (a new unique keypair, on which the pubkey is the `program_id`).

6. Deploy the program to the selected blockchain cluster by creating transactions containing the program's byte-code.

Expand Down
26 changes: 22 additions & 4 deletions chapters/04_pda.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@

<br>

* PDAs can be thought a way of mimic-ing web2's databases writing schemmes. For instance, a dApp that needs to update its own data without having a 'client' authorizing those changes.

* PDAs are addresses with special properties. They are not public keys (so they don't have an associated public key).

* * PDAs provide a mechanism to build hashmap-like structures on-chain, allowing programs to sign instructions.
* `findProgramAddress` will deterministically derive a PDA from a programId and seeds (collection of bytes)
* `findProgramAddress` will deterministically derive a PDA from a `program_id`` and seeds (collection of bytes)
* A bump (one byte) is used to push a potential PDA off the ed25519 elliptic curve.
* Programs can sign for their PDAs by providing the seeds and bump to invoke_signed.

Expand All @@ -24,19 +25,33 @@

### Generating PDA

<br>

#### *When you use seeds to derive a public key, there is a chance that the seed you use and the public derived from them have an associated private key (depicted by the ed2559 elliptic curve).*
#### *So if the seeds derive a private key that exists in the curve, Solana will add an additional integer (a bump) to the seed list to make sure it bumps off the curve and cannot have a private key.*

<br>

<p align="center">
<img src="images/pda.png" width="60%" align="center" style="padding:1px;border:1px solid black;"/>
</p>





<br>

* PDA are created by hashing a number of seeds the user can choose with the `program_id`.

* Seeds can be anything: pubkey, strings, an array of numbers, etc.

* There is a 50% chance that this hash can result in a public key, so a bump has to be searched:
* There is a 50% chance that this hash can result in a public key. This is how a bump can be searched:

<br>


```rust
// pseudo code
fn find_pda(seeds, program_id) {
for bump in 0..256 {
let potential_pda = hash(seeds, bump, program_id);
Expand Down Expand Up @@ -73,7 +88,7 @@ fn find_pda(seeds, program_id) {

<br>

* PDAs are hashed from a bump, a program_id, and several seeds. These seeds can be used to build hashmap-like structures on-chain.
* PDAs are hashed from a bump, a `program_id`, and several seeds. These seeds can be used to build hashmap-like structures on-chain.

* With PDA, you can create structs that encode the information about a relationship between the user and some data account, so that PDA serves as the address:

Expand Down Expand Up @@ -171,4 +186,7 @@ const [pda, bump] = await web3.PublicKey.findProgramAddress(
* [Anchor Docs on PDA](https://www.anchor-lang.com/docs/pdas)
* [Solana's Cookbook on PDA](https://solanacookbook.com/core-concepts/pdas.html#facts)
* [Understanding PDAs, by brianfriel](https://www.brianfriel.xyz/understanding-program-derived-addresses/)
* [PDA, by Solana Bytes](https://www.youtube.com/watch?v=ZwFNPvqUclM&list=PLilwLeBwGuK51Ji870apdb88dnBr1Xqhm&index=8)
* [solana-program-library/token
/transfer-hook](https://github.com/igneous-labs/solana-program-library/tree/master/token/transfer-hook)

106 changes: 105 additions & 1 deletion chapters/07_transfer_hooks.md
Original file line number Diff line number Diff line change
@@ -1 +1,105 @@
# 🛹 Transfer Hooks [IN CONSTRUCTION]
# 🛹 Transfer Hooks

<br>

### tl; dr


<br>

* The Transfer Hook Interface, introduced within the Solana Program Library, allows token creators to "hook" additional custom logic into token transfers to shape the dynamics of users' and tokens' interactions.


<br>

---

### Token-2022 and Token Extensions

<br>

* Solana ecosystem faced a demand for enhanced token features:
- Before, expanding the capabilities of tokens required forking the existing Token Program, which introduced challenges due to architectural requirements for transactions specifying the involved programs and accounts.

* Token-2022 was created to add new token functionality, with minimal disruption to users, wallets, and dApps.
- This enabled Token Extensions, introducing a suite of program-level enhancements such as confidential transactions, customizable transfer logic, and enriched metadata.

<br>

---

### How Token Hooks Work

<br>

* Whenever the token is minted, the `Execute` instruction is triggered together with a Transfer Instruction (the custom logic).

* All accounts from the initial transfer are converted to read-only accounts (i.e., the signer privileges of the sender do not extend to the Transfer Hook program).

* Extra accounts required by `Execute` are stored in a predefined PDA that must be derived using the following seeds: `extra-account-metas` string, the mint account address, the transfer hook `program_id`.

<br>

```javascript
const [pda] = PublicKey.findProgramAddressSync(
[Buffer.from("extra-account-metas"),
mint.publicKey.toBuffer()],
program.programId,
);
```

<br>

---

### Specifications

<br>

* [The Transfer Hook interface specification](https://spl.solana.com/transfer-hook-interface/specification) includes two optional instructions and one required one; each uses a specific 8-byte discriminator at the start of its data.

* The `Execute` instruction is required, and this is the instruction in which custom transfer functionality lives.
- It contains the following parts:
- `Discriminator`, the first 8 bytes of the hash of "spl-transfer-hook-interface:execute"
- `Data`:
- `amount`: `u64`, the transfer amount
- `accounts:
- 1 []: Source token account
- 2 []: Mint
- 3 []: Destination token account
- 4 []: Source token account authority
- 5 []: Validation account

* `InitializeExtraAccountMetaList` is optional and intializes the validation account to store a list of extra required `AccountMeta` configurations for the `Execute` instruction.


* `UpdateExtraAccountMetaList` is optional and allows an on-chain program to update its list of required accounts for 1Execute`.


<br>

---

### Demos

<br>

* [Backend's demo 5: Transfer Hooks](https://github.com/urani-labs/solana-dev-onboarding-rs/tree/main/demos/backend/05_transfer_hooks)


<br>


---

### References

<br>


* [Transfer Hook Interface, by Solana Labs](https://spl.solana.com/transfer-hook-interface)
* [Token Transfer Hooks with Token Extensions on Solana](https://www.youtube.com/watch?v=Cc6CZWd-iMw)
* [How to use the Transfer Hooks Token Extension Part 2](https://www.youtube.com/watch?v=LsduWRtT3r8)
* [How to use the Transfer Hook extension, by Solana Labs](https://solana.com/developers/guides/token-extensions/transfer-hook)
* [Leveraging Transfer Hooks on Solana, by Leo](https://medium.com/@LeoOnSol/hooked-the-power-of-leveraging-transfer-hooks-on-solana-2b0f15770e64)
* [5 Ways to Get Hooked, by Solandy](https://www.youtube.com/watch?v=Sr-HiJdbf6w)
Binary file added chapters/images/pda.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion demos/backend/02_anchor_cpi/puppet-master/Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ url = "https://api.apr.dev"

[provider]
cluster = "Localnet"
wallet = "/Users/m/.config/solana/id.json"
wallet = "/Users/urani/.config/solana/id.json"

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
2 changes: 1 addition & 1 deletion demos/backend/02_anchor_cpi/puppet/Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ url = "https://api.apr.dev"

[provider]
cluster = "Localnet"
wallet = "/Users/m/.config/solana/id.json"
wallet = "/Users/urani/.config/solana/id.json"

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
Expand Down
2 changes: 1 addition & 1 deletion demos/backend/03_anchor_pda/game/Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ url = "https://api.apr.dev"

[provider]
cluster = "Localnet"
wallet = "/Users/m/.config/solana/id.json"
wallet = "/Users/urani/.config/solana/id.json"

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
2 changes: 1 addition & 1 deletion demos/backend/04_pda_and_cpi/puppet-master/Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ url = "https://api.apr.dev"

[provider]
cluster = "Localnet"
wallet = "/Users/m/.config/solana/id.json"
wallet = "/Users/urani/.config/solana/id.json"

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
2 changes: 1 addition & 1 deletion demos/backend/04_pda_and_cpi/puppet/Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ url = "https://api.apr.dev"

[provider]
cluster = "Localnet"
wallet = "/Users/m/.config/solana/id.json"
wallet = "/Users/urani/.config/solana/id.json"

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
18 changes: 18 additions & 0 deletions demos/backend/05_transfer_hooks/Anchor.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[toolchain]

[features]
seeds = false
skip-lint = false

[programs.localnet]
vesting_template = "FohntM2s5Z1uqyDbBAu1CHxYh9umzeiRSKr198rSSdvT"

[registry]
url = "https://api.apr.dev"

[provider]
cluster = "Localnet"
wallet = "/Users/leo/.config/solana/id.json"

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
13 changes: 13 additions & 0 deletions demos/backend/05_transfer_hooks/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[workspace]
members = [
"programs/*"
]

[profile.release]
overflow-checks = true
lto = "fat"
codegen-units = 1
[profile.release.build-override]
opt-level = 3
incremental = false
codegen-units = 1
12 changes: 12 additions & 0 deletions demos/backend/05_transfer_hooks/migrations/deploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Migrations are an early feature. Currently, they're nothing more than this
// single deploy script that's invoked from the CLI, injecting a provider
// configured from the workspace's Anchor.toml.

const anchor = require("@coral-xyz/anchor");

module.exports = async function (provider) {
// Configure client to use the provider.
anchor.setProvider(provider);

// Add your deploy script here.
};
20 changes: 20 additions & 0 deletions demos/backend/05_transfer_hooks/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"scripts": {
"lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w",
"lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check"
},
"dependencies": {
"@coral-xyz/anchor": "^0.29.0",
"@solana/spl-token": "^0.4.1"
},
"devDependencies": {
"@types/bn.js": "^5.1.0",
"@types/chai": "^4.3.0",
"@types/mocha": "^9.0.0",
"chai": "^4.3.4",
"mocha": "^9.0.3",
"prettier": "^2.6.2",
"ts-mocha": "^10.0.0",
"typescript": "^4.3.5"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "vesting-template"
version = "0.1.0"
description = "Created with Anchor"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]
name = "vesting_template"

[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []

[dependencies]
anchor-lang = { version = "0.29.0", features = ["init-if-needed"]}
anchor-spl = { version = "0.29.0", features = ["token", "metadata"]}
spl-tlv-account-resolution = "0.5.0"
spl-transfer-hook-interface = "0.5.0"
toml_edit = "=0.21.0"
solana-program = "=1.17"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []
Loading

0 comments on commit ae5740c

Please sign in to comment.