Compare commits
274 Commits
d9d4214341
...
a15fee9d37
Author | SHA1 | Date |
---|---|---|
Sebastian Bor | a15fee9d37 | |
dependabot[bot] | 718a6b6074 | |
dependabot[bot] | ff46681e80 | |
dependabot[bot] | b0a58d2ad8 | |
dependabot[bot] | b513f371d8 | |
dependabot[bot] | e1dc335ef5 | |
dependabot[bot] | 9164ba11c8 | |
Jon Cinque | 24d54db8b8 | |
dependabot[bot] | 68217f399c | |
dependabot[bot] | 03eb7930f7 | |
dependabot[bot] | c7563ba31d | |
dependabot[bot] | d404916d70 | |
dependabot[bot] | 5a1c6461cf | |
dependabot[bot] | 6bbb497aa5 | |
dependabot[bot] | ba083343ab | |
Jon Cinque | 3c070a45e5 | |
dependabot[bot] | 8f7ef32189 | |
Jon Cinque | d01730a7aa | |
dependabot[bot] | ca0f3377ad | |
dependabot[bot] | 5c3d43554a | |
dependabot[bot] | 986291e04d | |
Jon Cinque | e153162541 | |
dependabot[bot] | 0648514264 | |
dependabot[bot] | 0aa9e5c5d5 | |
dependabot[bot] | 9591ac0b14 | |
dependabot[bot] | 2395b30187 | |
dependabot[bot] | 2931db72c6 | |
dependabot[bot] | 87ad50be88 | |
dependabot[bot] | dce34d6f23 | |
dependabot[bot] | 97cd2b129b | |
dependabot[bot] | 23bc53e8b1 | |
dependabot[bot] | f2490dc80f | |
Evan B | cb107edc97 | |
dependabot[bot] | f4cc2844a0 | |
dependabot[bot] | 285a976641 | |
dependabot[bot] | c8aefde878 | |
dependabot[bot] | cfc3938d10 | |
dependabot[bot] | 3a9d461def | |
dependabot[bot] | 733f4044d1 | |
dependabot[bot] | 68ead44596 | |
dependabot[bot] | 7a7dbf742c | |
dependabot[bot] | 79821ac9cc | |
dependabot[bot] | 4caf143743 | |
dependabot[bot] | ef4ba61da4 | |
dependabot[bot] | bb7474fe17 | |
dependabot[bot] | 6075365a4e | |
Jon Cinque | 08114a8f6d | |
dependabot[bot] | f3afc01575 | |
dependabot[bot] | 68b775549e | |
dependabot[bot] | 798629f73c | |
dependabot[bot] | f0405323a2 | |
dependabot[bot] | 50464f637a | |
dependabot[bot] | 1e054cc165 | |
dependabot[bot] | 72347e02b7 | |
dependabot[bot] | 536f194996 | |
dependabot[bot] | 2b942dcd24 | |
dependabot[bot] | 14ce4840e0 | |
dependabot[bot] | 1cb93a59d8 | |
dependabot[bot] | 558bd7d9ba | |
dependabot[bot] | a602303275 | |
dependabot[bot] | 9c7271ca0f | |
dependabot[bot] | b3315ede3a | |
dependabot[bot] | 755290a3de | |
dependabot[bot] | 62e09c46e8 | |
dependabot[bot] | 75f8f88b88 | |
dependabot[bot] | eeb34c0bfc | |
dependabot[bot] | 0a5cbf888c | |
dependabot[bot] | 2eae061493 | |
dependabot[bot] | 4e9ed5d2b5 | |
dependabot[bot] | a0791dc0dd | |
dependabot[bot] | 2d3cf089e4 | |
dependabot[bot] | 7368c4bea4 | |
dependabot[bot] | 8eb984b331 | |
Syed Aabis Akhtar | 957263073e | |
Jon Cinque | d92664f6df | |
Jon Cinque | de05760e78 | |
Mateusz Burzyński | ff8710e4db | |
Jon Cinque | 7aa07f2e83 | |
gh | d7349d96a1 | |
Jon Cinque | 4349f16085 | |
Nick Frostbutter | a5a061f0c7 | |
hana | 9f6a36abb3 | |
Sebastian Bor | 139ed61e84 | |
dependabot[bot] | 9ab3cdc93a | |
dependabot[bot] | 419456deda | |
Jon Cinque | 7af3a700ff | |
ilmoi | 80624e7a1a | |
Yihau Chen | e3cb109afa | |
ilmoi | 54db84ae21 | |
dependabot[bot] | f912279be8 | |
dependabot[bot] | c6b11ac562 | |
dependabot[bot] | 23d9ffb6de | |
dependabot[bot] | 6e8246819f | |
dependabot[bot] | 2dfa00bc8c | |
dependabot[bot] | 2458812065 | |
Jon Cinque | d5ddac0e88 | |
Jeff Biseda | ddc76a6c0b | |
Jon Cinque | 1d3d7be5f0 | |
Noah Gundotra | 0ac5d24310 | |
Jon Cinque | 9270fd72c5 | |
0xkrabbypatty | ba025282b6 | |
dependabot[bot] | 12106f2904 | |
Alexey Skibin | e6f5066a46 | |
chalda | 9587db4e12 | |
fedorerd | 4e2fd75597 | |
chalda | 4b76dc9d67 | |
hana | 1e82a405d8 | |
Shubham Singh | 776d0624c1 | |
Pierre | 88ff889afd | |
Jon Cinque | 9dff3ed3a2 | |
Jon Cinque | 5c236e0584 | |
Jon Cinque | 48ea2153da | |
Jon Cinque | 1a65a0d03a | |
0xShuk | 7fe04d880b | |
samkim-crypto | 581d4cf456 | |
dependabot[bot] | 4761fd94ba | |
hana | 0e48b25a8f | |
dependabot[bot] | 5359039e15 | |
Kevin Ji | b9c5c3e168 | |
Giovanni Napoli | 325f3d26fb | |
Noah Gundotra | 1892778b9d | |
Noah Gundotra | 00ca869b13 | |
Khac Vy | 32379d14be | |
samkim-crypto | 312e5e190a | |
samkim-crypto | a4a9ae0b57 | |
samkim-crypto | 95a079a6e0 | |
samkim-crypto | ecd5c45726 | |
mvines | 331c0c20f6 | |
samkim-crypto | 73673b6b30 | |
samkim-crypto | c131e3c855 | |
Marquelle Nesbitt | 3db25f2686 | |
Jon Cinque | 9c1ee63f97 | |
samkim-crypto | c27a603425 | |
Jon Cinque | dc24cee50a | |
gr8den | eb7d32612f | |
Tyera | 9b5e63c39f | |
hana | 1caeadd65d | |
Noah Gundotra | bd216c8103 | |
Sebastian Bor | 96219bc677 | |
Austin Adams | acb4306ab0 | |
dependabot[bot] | e564c79417 | |
dependabot[bot] | 4ade9b28b3 | |
Jon Cinque | ea891fe0df | |
Jon Cinque | a23714ecc5 | |
dependabot[bot] | 86ceb0b756 | |
dependabot[bot] | 34017aa84b | |
dependabot[bot] | ce1d998fb1 | |
dependabot[bot] | 5426662432 | |
dependabot[bot] | 7266e5fc9b | |
Jon Cinque | 34b57d62fe | |
dependabot[bot] | 0f95a25274 | |
dependabot[bot] | 1ebf9279b6 | |
Athar Mohammad | 59ad74cd94 | |
Jon Cinque | d0bd3342e2 | |
Jon Cinque | 25a4a67a92 | |
Jon Cinque | 3249a0c3b6 | |
dependabot[bot] | f17995c06e | |
chalda | 4f648f9a02 | |
chalda | f013deb868 | |
dependabot[bot] | a376c3074f | |
dependabot[bot] | a25b2a9956 | |
dependabot[bot] | 9281857552 | |
dependabot[bot] | cd330aefe2 | |
Jon Cinque | 3136a49850 | |
dependabot[bot] | 4da4314b59 | |
samkim-crypto | 394c930f18 | |
Jon Cinque | 49c7e7df53 | |
Noah Gundotra | 66b83a837c | |
jon wong | 182a1679a9 | |
dependabot[bot] | 1addf7422c | |
dependabot[bot] | f948579954 | |
dependabot[bot] | c3f46a478d | |
omahs | c73fe57b92 | |
Jon Cinque | 62d83db283 | |
Jon Cinque | e71ea00729 | |
Jon Cinque | 209f6126e2 | |
Jon Cinque | ff58a068e2 | |
Jon Cinque | 3c02459f4d | |
Jon Cinque | 6394e49a35 | |
joeaba | 891b4bdad8 | |
Jon Cinque | 4a9b242cb1 | |
joeaba | 49335118dd | |
Jon Cinque | 2cdef76cc9 | |
Jon Cinque | 9da538b96f | |
Jon Cinque | cc3a56d1e4 | |
Jon Cinque | ab67891480 | |
Anoushk Kharangate | 4c0b1101a5 | |
Jon Cinque | c7a1e985ac | |
Sammy Harris | c6c83982d7 | |
Jon Cinque | c4269cb989 | |
dependabot[bot] | b9858fdee9 | |
Jon Cinque | 809bcfee41 | |
Jon Cinque | 88684d52a2 | |
Noah Gundotra | 9fe87ded65 | |
Jon Cinque | 4bbfdabda4 | |
Jon Cinque | fad5d585da | |
Sebastian Bor | f600c9035d | |
Noah Gundotra | e3d2b993ba | |
Alexander Ray | 6ab15b340e | |
Noah Gundotra | b33bcc055e | |
Noah Gundotra | bf14dc39f6 | |
dependabot[bot] | 9853e0a4f0 | |
Jon Cinque | f825f1d106 | |
Jon Cinque | 9fd9805345 | |
mPaella | befea2c0a8 | |
Jon Cinque | 6793256b9e | |
Jon Cinque | 64b844ace2 | |
hana | 56b1a95230 | |
hanako mumei | 8dde48e63e | |
hanako mumei | f5133d6847 | |
hanako mumei | 0e530fa080 | |
Yihau Chen | bef10795e1 | |
Michael Vines | 8caa54349c | |
dependabot[bot] | d2cac80ef2 | |
dependabot[bot] | 3f6b5e4836 | |
dependabot[bot] | 280a589907 | |
dependabot[bot] | e0548c1e06 | |
dependabot[bot] | d36fd08674 | |
Jon Cinque | eba709b931 | |
Jon Cinque | 4a8e7a0034 | |
Jon Cinque | ef17887914 | |
Jon Cinque | 9aac29c250 | |
samkim-crypto | e0b70a9749 | |
samkim-crypto | b9aba3fb8d | |
chalda | 19b8fae56c | |
samkim-crypto | 6386f24253 | |
samkim-crypto | ac02e0afde | |
Noah Gundotra | 4ffbc72519 | |
Noah Prince | 49883fba9f | |
samkim-crypto | a083ce4f17 | |
samkim-crypto | ba2b7951fc | |
hanako mumei | 9a0ecb3814 | |
Jon Cinque | 9f050c7c49 | |
samkim-crypto | c51ccf05c2 | |
hanako mumei | 72a41d4a22 | |
hanako mumei | d1dfa2d233 | |
hanako mumei | 980065ec6c | |
dependabot[bot] | d3e4ca7a6c | |
Sebastian Bor | c750e03f50 | |
Jordan Sexton | 1f58b759d8 | |
Jon Cinque | 26aed39c54 | |
Dmitri Makarov | 27da0acfbe | |
dependabot[bot] | 1d6902db34 | |
dependabot[bot] | ac96df0745 | |
dependabot[bot] | 9572d6162f | |
dependabot[bot] | e5b0ed8cf5 | |
dependabot[bot] | 56cdef9ee8 | |
Jon Cinque | 804204f505 | |
hanako mumei | 1dcdef3a01 | |
Jon Cinque | ae994afa3e | |
Jon Cinque | cacf4e9038 | |
dependabot[bot] | 5279302b38 | |
dependabot[bot] | 3372eb6c0d | |
Jon Cinque | 7ef7535ef2 | |
Jon Cinque | 5f4943802b | |
Jon Cinque | 77a297c2c7 | |
Jon Cinque | 5d16d5b926 | |
Jon Cinque | edd6a85223 | |
zhaogang | 8922d20697 | |
samkim-crypto | 39a6d59f27 | |
Austin Adams | ed8522b56e | |
samkim-crypto | bb404a6af5 | |
Jon Cinque | fd92ccf9e9 | |
Jon Cinque | 56640720cf | |
Jon Cinque | da375306d2 | |
samkim-crypto | c7fbd4b69b | |
samkim-crypto | 6a8cf4f938 | |
Athar Mohammad | c7c12f987c | |
Tyera | 979fdc8b7b | |
Jon Cinque | 30f60563ba | |
Jon Cinque | 1566564b04 | |
Jon Cinque | 7d4ab6028b | |
Jon Cinque | 792ffbc518 | |
Jon Cinque | d8e3803fe4 |
|
@ -1,16 +1,18 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: npm
|
||||
directory: "/token/js"
|
||||
- package-ecosystem: cargo
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "01:00"
|
||||
timezone: America/Los_Angeles
|
||||
open-pull-requests-limit: 3
|
||||
labels:
|
||||
- "automerge"
|
||||
open-pull-requests-limit: 6
|
||||
ignore:
|
||||
- dependency-name: "cbindgen"
|
||||
- package-ecosystem: npm
|
||||
directory: "/token-lending/js"
|
||||
directory: "/token/js"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "02:00"
|
||||
|
@ -19,7 +21,7 @@ updates:
|
|||
labels:
|
||||
- "automerge"
|
||||
- package-ecosystem: npm
|
||||
directory: "/token-swap/js"
|
||||
directory: "/token-lending/js"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "03:00"
|
||||
|
@ -27,14 +29,39 @@ updates:
|
|||
open-pull-requests-limit: 3
|
||||
labels:
|
||||
- "automerge"
|
||||
- package-ecosystem: cargo
|
||||
directory: "/"
|
||||
- package-ecosystem: npm
|
||||
directory: "/token-swap/js"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "04:00"
|
||||
timezone: America/Los_Angeles
|
||||
open-pull-requests-limit: 3
|
||||
labels:
|
||||
- "automerge"
|
||||
- package-ecosystem: npm
|
||||
directory: "/stake-pool/js"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "05:00"
|
||||
timezone: America/Los_Angeles
|
||||
open-pull-requests-limit: 3
|
||||
ignore:
|
||||
- dependency-name: "cbindgen"
|
||||
labels:
|
||||
- "automerge"
|
||||
- package-ecosystem: npm
|
||||
directory: "/name-service/js"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "06:00"
|
||||
timezone: America/Los_Angeles
|
||||
open-pull-requests-limit: 3
|
||||
labels:
|
||||
- "automerge"
|
||||
- package-ecosystem: npm
|
||||
directory: "/memo/js"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "07:00"
|
||||
timezone: America/Los_Angeles
|
||||
open-pull-requests-limit: 3
|
||||
labels:
|
||||
- "automerge"
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
question:
|
||||
issues:
|
||||
# Post a comment, `{issue-author}` is an optional placeholder
|
||||
comment: >
|
||||
Hi @{issue-author},
|
||||
|
||||
|
||||
Thanks for your question!
|
||||
|
||||
|
||||
We want to make sure to keep signal strong in the GitHub issue tracker – to make sure
|
||||
that it remains the best place to track issues that affect the development of Solana itself.
|
||||
|
||||
|
||||
Questions like yours deserve a purpose-built Q&A forum. Unless there exists evidence that
|
||||
this is a bug with Solana itself, please post your question to the Solana Stack Exchange
|
||||
using this link: https://solana.stackexchange.com/questions/ask
|
||||
|
||||
|
||||
---
|
||||
|
||||
_This
|
||||
[automated message](https://github.com/solana-labs/solana-program-library/blob/master/.github/label-actions.yml)
|
||||
is a result of having added the ‘question’ tag_.
|
||||
|
||||
# Close the issue
|
||||
close: true
|
|
@ -0,0 +1,15 @@
|
|||
name: "Issue Label Actions"
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled, unlabeled]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
action:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/label-actions@v2
|
|
@ -57,10 +57,18 @@ jobs:
|
|||
- name: Build and test
|
||||
run: ./ci/cargo-test-sbf.sh name-service
|
||||
|
||||
- name: Upload programs
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: name-service-programs
|
||||
path: "target/deploy/*.so"
|
||||
if-no-files-found: error
|
||||
|
||||
js-test:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NODE_VERSION: 14.x
|
||||
needs: cargo-test-sbf
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ env.NODE_VERSION }}
|
||||
|
@ -73,4 +81,9 @@ jobs:
|
|||
key: node-${{ hashFiles('name-service/js/yarn.lock') }}
|
||||
restore-keys: |
|
||||
node-
|
||||
- name: Download programs
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: name-service-programs
|
||||
path: target/deploy
|
||||
- run: ./ci/js-test-name-service.sh
|
||||
|
|
|
@ -64,6 +64,9 @@ jobs:
|
|||
- name: Build and test ATA
|
||||
run: ./ci/cargo-test-sbf.sh associated-token-account
|
||||
|
||||
- name: Build and test transfer hook example
|
||||
run: ./ci/cargo-test-sbf.sh token/transfer-hook-example
|
||||
|
||||
- name: Build and test token-2022 with "serde" activated
|
||||
run: |
|
||||
cargo +"${{ env.RUST_STABLE }}" test \
|
||||
|
@ -86,7 +89,7 @@ jobs:
|
|||
|
||||
- name: Set env vars
|
||||
run: |
|
||||
echo "RUST_STABLE_VERSION=1.65.0" >> $GITHUB_ENV
|
||||
echo "RUST_STABLE_VERSION=1.66.1" >> $GITHUB_ENV
|
||||
source ci/rust-version.sh
|
||||
echo "RUST_STABLE=$rust_stable" >> $GITHUB_ENV
|
||||
source ci/solana-version.sh
|
||||
|
@ -94,7 +97,7 @@ jobs:
|
|||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_STABLE }}
|
||||
toolchain: ${{ env.RUST_STABLE_VERSION }}
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
|
@ -103,7 +106,7 @@ jobs:
|
|||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
key: cargo-build-${{ hashFiles('**/Cargo.lock') }}-${{ env.RUST_STABLE }}
|
||||
key: cargo-build-${{ hashFiles('**/Cargo.lock') }}-${{ env.RUST_STABLE_VERSION }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
|
@ -114,7 +117,9 @@ jobs:
|
|||
- name: Build and test token-2022 twoxtx (TEMPORARY)
|
||||
run: |
|
||||
./token/twoxtx-setup.sh
|
||||
./ci/cargo-test-sbf.sh token/program-2022-test
|
||||
./token/twoxtx-solana/cargo-build-sbf --manifest-path token/program-2022/Cargo.toml
|
||||
./token/twoxtx-solana/cargo-build-sbf --manifest-path instruction-padding/program/Cargo.toml
|
||||
./token/twoxtx-solana/cargo-test-sbf --manifest-path token/program-2022-test/Cargo.toml -- --nocapture
|
||||
|
||||
js-test:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
@ -24,3 +24,4 @@ jobs:
|
|||
./build.sh
|
||||
env:
|
||||
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
|
||||
VERCEL_SCOPE: ${{ secrets.VERCEL_SCOPE }}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
anchor_version = "0.24.2"
|
||||
solana_version = "1.14.6"
|
||||
solana_version = "1.14.12"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"governance/program",
|
||||
"governance/chat/program",
|
||||
"memo/program",
|
||||
"stake-pool/program",
|
||||
"token/program",
|
||||
|
@ -19,6 +20,7 @@ wallet = "~/.config/solana/id.json"
|
|||
|
||||
[programs.mainnet]
|
||||
spl_governance = "GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw"
|
||||
spl_governance_chat = "gCHAtYKrUUktTVzE4hEnZdLV4LXrdBf6Hh9qMaJALET"
|
||||
spl_stake_pool = "SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy"
|
||||
spl_token = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
|
||||
spl_token_2022 = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
24
Cargo.toml
24
Cargo.toml
|
@ -1,3 +1,6 @@
|
|||
[profile.dev]
|
||||
split-debuginfo = "unpacked"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"associated-token-account/program",
|
||||
|
@ -9,6 +12,7 @@ members = [
|
|||
"examples/rust/logging",
|
||||
"examples/rust/sysvar",
|
||||
"examples/rust/transfer-lamports",
|
||||
"examples/rust/transfer-tokens",
|
||||
"feature-proposal/program",
|
||||
"feature-proposal/cli",
|
||||
"governance/addin-mock/program",
|
||||
|
@ -21,12 +25,15 @@ members = [
|
|||
"libraries/math",
|
||||
"libraries/concurrent-merkle-tree",
|
||||
"libraries/merkle-tree-reference",
|
||||
"libraries/tlv-account-resolution",
|
||||
"libraries/type-length-value",
|
||||
"memo/program",
|
||||
"name-service/program",
|
||||
"managed-token/program",
|
||||
"record/program",
|
||||
"shared-memory/program",
|
||||
"stake-pool/cli",
|
||||
"stake-pool/single-pool",
|
||||
"stake-pool/program",
|
||||
"stateless-asks/program",
|
||||
"token-lending/cli",
|
||||
|
@ -39,23 +46,10 @@ members = [
|
|||
"token/program",
|
||||
"token/program-2022",
|
||||
"token/program-2022-test",
|
||||
"token/transfer-hook-example",
|
||||
"token/transfer-hook-interface",
|
||||
"token/client",
|
||||
"utils/cgen",
|
||||
"utils/test-client",
|
||||
"token-lending/flash_loan_receiver",
|
||||
]
|
||||
exclude = [
|
||||
"farms/farm-client",
|
||||
"farms/farm-ctrl",
|
||||
"farms/farm-rpc",
|
||||
"farms/farm-sdk",
|
||||
"farms/fund",
|
||||
"farms/router-main",
|
||||
"farms/router-raydium",
|
||||
"farms/router-saber",
|
||||
"farms/router-orca",
|
||||
"farms/vaults",
|
||||
]
|
||||
|
||||
[profile.dev]
|
||||
split-debuginfo = "unpacked"
|
||||
|
|
129
README.md
129
README.md
|
@ -1,21 +1,97 @@
|
|||
[![Build status][travis-image]][travis-url]
|
||||
|
||||
[travis-image]:
|
||||
https://travis-ci.org/solana-labs/solana-program-library.svg?branch=master
|
||||
[travis-url]: https://travis-ci.org/solana-labs/solana-program-library
|
||||
|
||||
# Solana Program Library
|
||||
|
||||
The Solana Program Library (SPL) is a collection of on-chain programs targeting
|
||||
the [Sealevel parallel
|
||||
runtime](https://medium.com/solana-labs/sealevel-parallel-processing-thousands-of-smart-contracts-d814b378192).
|
||||
These programs are tested against Solana's implementation of Sealevel,
|
||||
solana-runtime, and deployed to its mainnet. As others implement Sealevel, we
|
||||
will graciously accept patches to ensure the programs here are portable across
|
||||
all implementations.
|
||||
solana-runtime, and some are deployed to Mainnet Beta. As others implement
|
||||
Sealevel, we will graciously accept patches to ensure the programs here are
|
||||
portable across all implementations.
|
||||
|
||||
For more information see the [SPL documentation](https://spl.solana.com) and the [Token TypeDocs](https://solana-labs.github.io/solana-program-library/token/js/).
|
||||
|
||||
## Audits
|
||||
|
||||
Only a subset of programs within the Solana Program Library repo are deployed to
|
||||
the Solana Mainnet Beta. Currently, this includes:
|
||||
|
||||
| Program | Last Audit Date | Version |
|
||||
| --- | --- | --- |
|
||||
| [token](https://github.com/solana-labs/solana-program-library/tree/master/token/program) | 2022-08-04 (Peer review) | [3.4.0](https://github.com/solana-labs/solana-program-library/releases/tag/token-v3.4.0) |
|
||||
| [associated-token-account](https://github.com/solana-labs/solana-program-library/tree/master/associated-token-account/program) | 2022-08-04 (Peer review) | [1.1.0](https://github.com/solana-labs/solana-program-library/releases/tag/associated-token-account-v1.1.0) |
|
||||
| [token-2022](https://github.com/solana-labs/solana-program-library/tree/master/token/program-2022) | [2023-02-10](https://github.com/solana-labs/security-audits/blob/master/spl/TrailOfBitsToken2022Audit-2023-02-10.pdf) | [0.5.0](https://github.com/solana-labs/solana-program-library/releases/tag/token-2022-v0.5.0) |
|
||||
| [governance](https://github.com/solana-labs/solana-program-library/tree/master/governance/program) | N/A | [3.1.0](https://github.com/solana-labs/solana-program-library/releases/tag/governance-v3.1.0) |
|
||||
| [stake-pool](https://github.com/solana-labs/solana-program-library/tree/master/stake-pool/program) | [2023-01-31](https://github.com/solana-labs/security-audits/blob/master/spl/NeodymeStakePoolAudit-2023-01-31.pdf) | [1.0.0]() |
|
||||
| [account-compression](https://github.com/solana-labs/solana-program-library/tree/master/account-compression/programs/account-compression) | [2022-12-05](https://github.com/solana-labs/security-audits/blob/master/spl/OtterSecAccountCompressionAudit-2022-12-03.pdf) | [0.1.3](https://github.com/solana-labs/solana-program-library/releases/tag/account-compression-v0.1.3) |
|
||||
| [shared-memory](https://github.com/solana-labs/solana-program-library/tree/master/shared-memory/program) | [2021-02-25](https://github.com/solana-labs/security-audits/blob/master/spl/KudelskiTokenSwapSharedMemAudit-2021-02-25.pdf) | [1.0.0](https://github.com/solana-labs/solana-program-library/commit/b40e0dd3fd6c0e509dc1e8dd3da0a6d609035bbd) |
|
||||
| [feature-proposal](https://github.com/solana-labs/solana-program-library/tree/master/feature-proposal/program) | Not audited | [1.0.0](https://github.com/solana-labs/solana-program-library/releases/tag/feature-proposal-v1.0.0) |
|
||||
| [name-service](https://github.com/solana-labs/solana-program-library/tree/master/name-service/program) | Not audited | [0.3.0](https://github.com/solana-labs/solana-program-library/releases/tag/name-service-v0.3.0) |
|
||||
| [memo](https://github.com/solana-labs/solana-program-library/tree/master/memo/program) | Not audited | [3.0.0](https://github.com/solana-labs/solana-program-library/releases/tag/memo-v3.0.0) |
|
||||
|
||||
All other programs may be updated from time to time. These programs are not
|
||||
audited, so fork and deploy them at your own risk. Here is the full list of
|
||||
unaudited programs:
|
||||
|
||||
* [binary-option](https://github.com/solana-labs/solana-program-library/tree/master/binary-option/program)
|
||||
* [binary-oracle-pair](https://github.com/solana-labs/solana-program-library/tree/master/binary-oracle-pair/program)
|
||||
* [instruction-padding](https://github.com/solana-labs/solana-program-library/tree/master/instruction-padding/program)
|
||||
* [managed-token](https://github.com/solana-labs/solana-program-library/tree/master/managed-token/program)
|
||||
* [record](https://github.com/solana-labs/solana-program-library/tree/master/record/program)
|
||||
* [stateless-asks](https://github.com/solana-labs/solana-program-library/tree/master/stateless-asks/program)
|
||||
* [token-lending](https://github.com/solana-labs/solana-program-library/tree/master/token-lending/program)
|
||||
* [token-swap](https://github.com/solana-labs/solana-program-library/tree/master/token-swap/program)
|
||||
* [token-upgrade](https://github.com/solana-labs/solana-program-library/tree/master/token-upgrade/program)
|
||||
|
||||
More information about the repository's security policy at
|
||||
[SECURITY.md](https://github.com/solana-labs/solana-program-library/tree/master/SECURITY.md).
|
||||
|
||||
The [security-audits repo](https://github.com/solana-labs/security-audits) contains
|
||||
all past and present program audits.
|
||||
|
||||
## Program Packages
|
||||
|
||||
| Package | Description | Version | Docs |
|
||||
| :-- | :-- | :--| :-- |
|
||||
| `spl-token` | ERC20-like token program on Solana | [![Crates.io](https://img.shields.io/crates/v/spl-token)](https://crates.io/crates/spl-token) | [![Docs.rs](https://docs.rs/spl-token/badge.svg)](https://docs.rs/spl-token) |
|
||||
| `spl-token-2022` | Token program compatible with `spl-token`, with extensions | [![Crates.io](https://img.shields.io/crates/v/spl-token-2022)](https://crates.io/crates/spl-token-2022) | [![Docs.rs](https://docs.rs/spl-token-2022/badge.svg)](https://docs.rs/spl-token-2022) |
|
||||
| `spl-associated-token-account` | Stateless protocol defining a canonical "associated" token account for a wallet | [![Crates.io](https://img.shields.io/crates/v/spl-associated-token-account)](https://crates.io/crates/spl-associated-token-account) | [![Docs.rs](https://docs.rs/spl-associated-token-account/badge.svg)](https://docs.rs/spl-associated-token-account) |
|
||||
| `spl-governance` | DAO program using tokens for voting | [![Crates.io](https://img.shields.io/crates/v/spl-governance)](https://crates.io/crates/spl-governance) | [![Docs.rs](https://docs.rs/spl-governance/badge.svg)](https://docs.rs/spl-governance) |
|
||||
| `spl-account-compression` | Program for managing compressed accounts stored in an off-chain merkle tree | [![Crates.io](https://img.shields.io/crates/v/spl-account-compression)](https://crates.io/crates/spl-account-compression) | [![Docs.rs](https://docs.rs/spl-account-compression/badge.svg)](https://docs.rs/spl-account-compression) |
|
||||
| `spl-feature-proposal` | Program using tokens to vote on enabling Solana network features | [![Crates.io](https://img.shields.io/crates/v/spl-feature-proposal)](https://crates.io/crates/spl-feature-proposal) | [![Docs.rs](https://docs.rs/spl-feature-proposal/badge.svg)](https://docs.rs/spl-feature-proposal) |
|
||||
| `spl-noop` | Program that does nothing, used for logging instruction data | [![Crates.io](https://img.shields.io/crates/v/spl-noop)](https://crates.io/crates/spl-noop) | [![Docs.rs](https://docs.rs/spl-noop/badge.svg)](https://docs.rs/spl-noop) |
|
||||
| `spl-memo` | Program for logging signed memos on-chain | [![Crates.io](https://img.shields.io/crates/v/spl-memo)](https://crates.io/crates/spl-memo) | [![Docs.rs](https://docs.rs/spl-memo/badge.svg)](https://docs.rs/spl-memo) |
|
||||
| `spl-name-service` | Program for managing ownership of data on-chain | [![Crates.io](https://img.shields.io/crates/v/spl-name-service)](https://crates.io/crates/spl-name-service) | [![Docs.rs](https://docs.rs/spl-name-service/badge.svg)](https://docs.rs/spl-name-service) |
|
||||
| `spl-shared-memory` | Program for sharing data between programs | [![Crates.io](https://img.shields.io/crates/v/spl-shared-memory)](https://crates.io/crates/spl-shared-memory) | [![Docs.rs](https://docs.rs/spl-shared-memory/badge.svg)](https://docs.rs/spl-shared-memory) |
|
||||
| `spl-stake-pool` | Program for pooling stake accounts, managed by another entity | [![Crates.io](https://img.shields.io/crates/v/spl-stake-pool)](https://crates.io/crates/spl-stake-pool) | [![Docs.rs](https://docs.rs/spl-stake-pool/badge.svg)](https://docs.rs/spl-stake-pool) |
|
||||
| `spl-instruction-padding` | Program to padding to other instructions | [![Crates.io](https://img.shields.io/crates/v/spl-instruction-padding)](https://crates.io/crates/spl-instruction-padding) | [![Docs.rs](https://docs.rs/spl-instruction-padding/badge.svg)](https://docs.rs/spl-instruction-padding) |
|
||||
| `spl-concurrent-merkle-tree` | Library for on-chain representation of merkle tree | [![Crates.io](https://img.shields.io/crates/v/spl-concurrent-merkle-tree)](https://crates.io/crates/spl-concurrent-merkle-tree) | [![Docs.rs](https://docs.rs/spl-concurrent-merkle-tree/badge.svg)](https://docs.rs/spl-concurrent-merkle-tree) |
|
||||
| `spl-math` | Library for on-chain math | [![Crates.io](https://img.shields.io/crates/v/spl-math)](https://crates.io/crates/spl-math) | [![Docs.rs](https://docs.rs/spl-math/badge.svg)](https://docs.rs/spl-math) |
|
||||
| `spl-token-lending` | Over-collateralized lending program for tokens | [![Crates.io](https://img.shields.io/crates/v/spl-token-lending)](https://crates.io/crates/spl-token-lending) | [![Docs.rs](https://docs.rs/spl-token-lending/badge.svg)](https://docs.rs/spl-token-lending) |
|
||||
| `spl-token-swap` | AMM for trading tokens | [![Crates.io](https://img.shields.io/crates/v/spl-token-swap)](https://crates.io/crates/spl-token-swap) | [![Docs.rs](https://docs.rs/spl-token-swap/badge.svg)](https://docs.rs/spl-token-swap) |
|
||||
| `spl-token-upgrade` | Protocol for burning one token type in exchange for another | [![Crates.io](https://img.shields.io/crates/v/spl-token-upgrade)](https://crates.io/crates/spl-token-upgrade) | [![Docs.rs](https://docs.rs/spl-token-upgrade/badge.svg)](https://docs.rs/spl-token-upgrade) |
|
||||
|
||||
## CLI Packages
|
||||
|
||||
| Package | Description | Version |
|
||||
| :-- | :-- | :--|
|
||||
| `spl-token-cli` | CLI for the token, token-2022, and associated-token-account programs | [![Crates.io](https://img.shields.io/crates/v/spl-token-cli)](https://crates.io/crates/spl-token-cli) |
|
||||
| `spl-stake-pool-cli` | CLI for the stake-pool program | [![Crates.io](https://img.shields.io/crates/v/spl-stake-pool-cli)](https://crates.io/crates/spl-stake-pool-cli) |
|
||||
| `spl-feature-proposal-cli` | CLI for the feature-proposal program | [![Crates.io](https://img.shields.io/crates/v/spl-feature-proposal-cli)](https://crates.io/crates/spl-feature-proposal-cli) |
|
||||
| `spl-token-lending-cli` | CLI for the token-lending program | [![Crates.io](https://img.shields.io/crates/v/spl-token-lending-cli)](https://crates.io/crates/spl-token-lending-cli) |
|
||||
| `spl-token-upgrade-cli` | CLI for the token-upgrade program | [![Crates.io](https://img.shields.io/crates/v/spl-token-upgrade-cli)](https://crates.io/crates/spl-token-upgrade-cli) |
|
||||
|
||||
## JavaScript Packages
|
||||
|
||||
| Package | Description | Version | Docs |
|
||||
| :-- | :-- | :--| :-- |
|
||||
| `@solana/spl-token` | Bindings for the token, token-2022, and associated-token-account programs | [![npm](https://img.shields.io/npm/v/@solana/spl-token.svg)](https://www.npmjs.com/package/@solana/spl-token) | [![Docs](https://img.shields.io/badge/docs-typedoc-blue)](https://solana-labs.github.io/solana-program-library/token/js) |
|
||||
| `@solana/spl-governance` | Bindings for the governance program | [![npm](https://img.shields.io/npm/v/@solana/spl-governance.svg)](https://www.npmjs.com/package/@solana/spl-governance) | N/A |
|
||||
| `@solana/spl-account-compression` | Bindings for the account-compression program | [![npm](https://img.shields.io/npm/v/@solana/spl-account-compression.svg)](https://www.npmjs.com/package/@solana/spl-account-compression) | [![Docs](https://img.shields.io/badge/docs-typedoc-blue)](https://solana-labs.github.io/solana-program-library/account-compression/sdk/docs) |
|
||||
| `@solana/spl-memo` | Bindings for the memo program | [![npm](https://img.shields.io/npm/v/@solana/spl-memo.svg)](https://www.npmjs.com/package/@solana/spl-memo) | N/A |
|
||||
| `@solana/spl-name-service` | Bindings for the name-service program | [![npm](https://img.shields.io/npm/v/@solana/spl-name-service.svg)](https://www.npmjs.com/package/@solana/spl-name-service) | N/A |
|
||||
| `@solana/spl-stake-pool` | Bindings for the stake-pool program | [![npm](https://img.shields.io/npm/v/@solana/spl-stake-pool.svg)](https://www.npmjs.com/package/@solana/spl-stake-pool) | N/A |
|
||||
| `@solana/spl-token-lending` | Bindings for the token-lending program | [![npm](https://img.shields.io/npm/v/@solana/spl-token-lending.svg)](https://www.npmjs.com/package/@solana/spl-token-lending) | N/A |
|
||||
| `@solana/spl-token-swap` | Bindings for the token-swap program | [![npm](https://img.shields.io/npm/v/@solana/spl-token-swap.svg)](https://www.npmjs.com/package/@solana/spl-token-swap) | N/A |
|
||||
|
||||
## Development
|
||||
|
||||
|
@ -68,6 +144,7 @@ Integration testing may be performed via the per-project .js bindings. See the
|
|||
[token program's js project](token/js) for an example.
|
||||
|
||||
### Common Issues
|
||||
|
||||
Solutions to a few issues you might run into are mentioned here.
|
||||
|
||||
1. `Failed to open: ../../deploy/spl_<program-name>.so`
|
||||
|
@ -75,9 +152,9 @@ Solutions to a few issues you might run into are mentioned here.
|
|||
Update your Rust and Cargo to the latest versions and re-run `cargo build-sbf` in the relevant `<program-name>` directory,
|
||||
or run it at the repository root to rebuild all on-chain programs.
|
||||
|
||||
2. [Error while loading shared libraries. (libssl.so.1.1)](https://github.com/project-serum/anchor/issues/1831)
|
||||
2. [Error while loading shared libraries. (libssl.so.1.1)](https://solana.stackexchange.com/q/3029/36)
|
||||
|
||||
A working solution was mentioned [here](https://github.com/project-serum/anchor/issues/1831#issuecomment-1109124934).
|
||||
A working solution was mentioned [here](https://solana.stackexchange.com/q/3029/36).
|
||||
Install libssl.
|
||||
```bash
|
||||
wget http://nz2.archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1l-1ubuntu1.2_amd64.deb
|
||||
|
@ -110,6 +187,7 @@ $ rustup toolchain install nightly-x86_64-apple-darwin
|
|||
|
||||
|
||||
## Release Process
|
||||
|
||||
SPL programs are currently tagged and released manually. Each program is
|
||||
versioned independently of the others, with all new development occurring on
|
||||
master. Once a program is tested and deemed ready for release:
|
||||
|
@ -149,13 +227,13 @@ to test the build. Then run `cargo publish`.
|
|||
|
||||
All claims, content, designs, algorithms, estimates, roadmaps,
|
||||
specifications, and performance measurements described in this project
|
||||
are done with the Solana Foundation's ("SF") best efforts. It is up to
|
||||
are done with the Solana Labs, Inc. (“SL”) best efforts. It is up to
|
||||
the reader to check and validate their accuracy and truthfulness.
|
||||
Furthermore nothing in this project constitutes a solicitation for
|
||||
investment.
|
||||
|
||||
Any content produced by SF or developer resources that SF provides, are
|
||||
for educational and inspiration purposes only. SF does not encourage,
|
||||
Any content produced by SL or developer resources that SL provides, are
|
||||
for educational and inspiration purposes only. SL does not encourage,
|
||||
induce or sanction the deployment, integration or use of any such
|
||||
applications (including the code comprising the Solana blockchain
|
||||
protocol) in violation of applicable laws or regulations and hereby
|
||||
|
@ -169,19 +247,10 @@ reader is or is working on behalf of a Specially Designated National
|
|||
(SDN) or a person subject to similar blocking or denied party
|
||||
prohibitions.
|
||||
|
||||
The reader should be aware that U.S. export control and sanctions laws
|
||||
prohibit U.S. persons (and other persons that are subject to such laws)
|
||||
from transacting with persons in certain countries and territories or
|
||||
that are on the SDN list. As a project based primarily on open-source
|
||||
software, it is possible that such sanctioned persons may nevertheless
|
||||
bypass prohibitions, obtain the code comprising the Solana blockchain
|
||||
protocol (or other project code or applications) and deploy, integrate,
|
||||
or otherwise use it. Accordingly, there is a risk to individuals that
|
||||
other persons using the Solana blockchain protocol may be sanctioned
|
||||
persons and that transactions with such persons would be a violation of
|
||||
U.S. export controls and sanctions law. This risk applies to
|
||||
individuals, organizations, and other ecosystem participants that
|
||||
deploy, integrate, or use the Solana blockchain protocol code directly
|
||||
(e.g., as a node operator), and individuals that transact on the Solana
|
||||
blockchain through light clients, third party interfaces, and/or wallet
|
||||
software.
|
||||
The reader should be aware that U.S. export control and sanctions laws
|
||||
prohibit U.S. persons (and other persons that are subject to such laws)
|
||||
from transacting with persons in certain countries and territories or
|
||||
that are on the SDN list. Accordingly, there is a risk to individuals
|
||||
that other persons using any of the code contained in this repo, or a
|
||||
derivation thereof, may be sanctioned persons and that transactions with
|
||||
such persons would be a violation of U.S. export controls and sanctions law.
|
||||
|
|
36
SECURITY.md
36
SECURITY.md
|
@ -6,25 +6,43 @@
|
|||
1. [Incident Response Process](#process)
|
||||
|
||||
<a name="reporting"></a>
|
||||
## Reporting security problems to Solana
|
||||
## Reporting security problems in the Solana Program Library
|
||||
|
||||
**DO NOT CREATE A GITHUB ISSUE** to report a security problem.
|
||||
|
||||
Instead please use this [Report a Vulnerability](https://github.com/solana-labs/solana-program-library/security/advisories/new) link.
|
||||
Provide a helpful title and detailed description of the problem.
|
||||
|
||||
If you haven't done so already, please **enable two-factor auth** in your GitHub account.
|
||||
|
||||
Expect a response as fast as possible in the advisory, typically within 72 hours.
|
||||
|
||||
--
|
||||
|
||||
If you do not receive a response in the advisory, send an email to
|
||||
security@solana.com with the full URL of the advisory you have created. DO NOT
|
||||
include attachments or provide detail sufficient for exploitation regarding the
|
||||
security issue in this email. **Only provide such details in the advisory**.
|
||||
|
||||
If you do not receive a response from security@solana.com please followup with
|
||||
the team directly. You can do this in the `#core-technology` channel of the
|
||||
[Solana Tech discord server](https://solana.com/discord), by pinging the admins
|
||||
in the channel and referencing the fact that you submitted a security problem.
|
||||
|
||||
**DO NOT CREATE AN ISSUE** to report a security problem. Instead, please send an
|
||||
email to security@solana.com and provide your github username so we can add you
|
||||
to a new draft security advisory for further discussion.
|
||||
|
||||
Expect a response as fast as possible, typically within 72 hours.
|
||||
|
||||
<a name="bounty"></a>
|
||||
## Security Bug Bounties
|
||||
We offer bounties for critical security issues. Please see the
|
||||
[Solana Security Bug Bounties](https://github.com/solana-labs/solana/security/policy#security-bug-bounties)
|
||||
The Solana Foundation offer bounties for critical Solana security issues. Please
|
||||
see the [Solana Security Bug
|
||||
Bounties](https://github.com/solana-labs/solana/security/policy#security-bug-bounties)
|
||||
for details on classes of bugs and payment amounts.
|
||||
|
||||
<a name="scope"></a>
|
||||
## Scope
|
||||
|
||||
Only a subset of programs within the Solana Program Library repo are deployed to
|
||||
mainnet-beta and maintained by the team. Currently, this includes:
|
||||
the Solana Mainnet Beta. Currently, this includes:
|
||||
|
||||
* [associated-token-account](https://github.com/solana-labs/solana-program-library/tree/master/associated-token-account/program)
|
||||
* [feature-proposal](https://github.com/solana-labs/solana-program-library/tree/master/feature-proposal/program)
|
||||
|
@ -51,4 +69,4 @@ may be affected and put you in touch the corresponding teams.
|
|||
|
||||
In case an incident is discovered or reported, the
|
||||
[Solana Security Incident Response Process](https://github.com/solana-labs/solana/security/policy#incident-response-process)
|
||||
will be followed to contain, respond and remediate.
|
||||
will be followed to contain, respond and remediate.
|
||||
|
|
|
@ -24,9 +24,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-access-control"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70f6ee9518f50ff4d434471ccf569186022bdd5ef65a21d14da3ea5231af944f"
|
||||
checksum = "cf7d535e1381be3de2c0716c0a1c1e32ad9df1042cddcf7bc18d743569e53319"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -38,9 +38,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-account"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32c92bcf5388b52676d990f85bbfd838a8f5672393135063a50dc79b2b837c79"
|
||||
checksum = "c3bcd731f21048a032be27c7791701120e44f3f6371358fc4261a7f716283d29"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -53,9 +53,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-constant"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0844974ac35e8ced62056b0d63777ebcdc5807438b8b189c881e2b647450b70a"
|
||||
checksum = "e1be64a48e395fe00b8217287f226078be2cf32dae42fdf8a885b997945c3d28"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"proc-macro2",
|
||||
|
@ -64,9 +64,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-error"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f7467345e67a6f1d4b862b9763a4160ad89d18c247b8c902807768f7b6e23df"
|
||||
checksum = "38ea6713d1938c0da03656ff8a693b17dc0396da66d1ba320557f07e86eca0d4"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"proc-macro2",
|
||||
|
@ -76,9 +76,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-event"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8774e4c1ac71f71a5aea7e4932fb69c30e3b8155c4fa59fd69401195434528a9"
|
||||
checksum = "d401f11efb3644285685f8339829a9786d43ed7490bb1699f33c478d04d5a582"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -89,9 +89,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-interface"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90eeb6e1c80f9f94fcef93a52813f6472186200e275e83cb3fac92b801de92f7"
|
||||
checksum = "c6700a6f5c888a9c33fe8afc0c64fd8575fa28d05446037306d0f96102ae4480"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -103,9 +103,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-program"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac515a7a5a4fea7fc768b1cec40ddb948e148ea657637c75f94f283212326cb9"
|
||||
checksum = "6ad769993b5266714e8939e47fbdede90e5c030333c7522d99a4d4748cf26712"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -116,9 +116,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-attribute-state"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43dc667b62ff71450f19dcfcc37b0c408fd4ddd89e8650368c2b0984b110603f"
|
||||
checksum = "4e677fae4a016a554acdd0e3b7f178d3acafaa7e7ffac6b8690cf4e171f1c116"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -129,9 +129,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-derive-accounts"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7354d583a06701d24800a8ec4c2b0491f62581a331af349205e23421e0b56643"
|
||||
checksum = "340beef6809d1c3fcc7ae219153d981e95a8a277ff31985bd7050e32645dc9a8"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anyhow",
|
||||
|
@ -142,9 +142,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-lang"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff5f57ec5e12fa6874b27f3d5c1f6f44302d3ad86c1266197ff7611bf6f5d251"
|
||||
checksum = "662ceafe667448ee4199a4be2ee83b6bb76da28566eee5cea05f96ab38255af8"
|
||||
dependencies = [
|
||||
"anchor-attribute-access-control",
|
||||
"anchor-attribute-account",
|
||||
|
@ -166,9 +166,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anchor-syn"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55aa1e680d9471342122ed5b6bc13bf5da473b0f7e4677d41a6954e5cc8ad155"
|
||||
checksum = "0418bcb5daac3b8cb1b60d8fdb1d468ca36f5509f31fb51179326fae1028fdcc"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bs58 0.3.1",
|
||||
|
@ -334,9 +334,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3"
|
|||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.10.0"
|
||||
version = "3.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
|
||||
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
|
||||
|
||||
[[package]]
|
||||
name = "bv"
|
||||
|
@ -350,18 +350,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.9.1"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdead85bdec19c194affaeeb670c0e41fe23de31459efd1c174d049269cf02cc"
|
||||
checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393"
|
||||
dependencies = [
|
||||
"bytemuck_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck_derive"
|
||||
version = "1.1.0"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "562e382481975bc61d11275ac5e62a19abd00b0547d99516a415336f183dcd0e"
|
||||
checksum = "1aca418a974d83d40a0c1f0c5cba6ff4bc28d8df099109ca459a2118d40b6322"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -851,9 +851,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.39"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
||||
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
@ -1111,9 +1111,9 @@ checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
|||
|
||||
[[package]]
|
||||
name = "solana-frozen-abi"
|
||||
version = "1.10.35"
|
||||
version = "1.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e73830d41b18597084df1d216b997c4a7396f0eb6674b4d411c30a4ba68d189"
|
||||
checksum = "0ab1187f9d6ec545567c7d3bdfc3c22c3886331aa4e49b2ad17c8ec5ec3b5334"
|
||||
dependencies = [
|
||||
"bs58 0.4.0",
|
||||
"bv",
|
||||
|
@ -1133,9 +1133,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "solana-frozen-abi-macro"
|
||||
version = "1.10.35"
|
||||
version = "1.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8dbca5d2413ddb4885b9bbaa14fdb9dd134539cabda8169a51b8990ee798bf4"
|
||||
checksum = "1427b307e619e53e33987967112b0bc5ae2983915e00a46720b589bffe7f924b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1145,9 +1145,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "solana-program"
|
||||
version = "1.10.35"
|
||||
version = "1.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "352ca385618fc739f45e98fde4b340cfd0570d3d1c31e1657a72fd71f51c6ead"
|
||||
checksum = "c77cdaa890475c0e21d23f24c5bcc12e0991cf73f2fe98fc1064f065365f6873"
|
||||
dependencies = [
|
||||
"base64 0.13.0",
|
||||
"bincode",
|
||||
|
@ -1187,9 +1187,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "solana-sdk-macro"
|
||||
version = "1.10.35"
|
||||
version = "1.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b65b8de75a25a8d7358b58d4a1899843f4c1e22fe5e760a4d1931adbc7975d9e"
|
||||
checksum = "df36a10a657b35accd2fd7ae097e9426cbbdbf8219a79e8a7d3be7b500be0e7e"
|
||||
dependencies = [
|
||||
"bs58 0.4.0",
|
||||
"proc-macro2",
|
||||
|
@ -1200,7 +1200,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "spl-account-compression"
|
||||
version = "0.1.4"
|
||||
version = "0.1.8"
|
||||
dependencies = [
|
||||
"anchor-lang",
|
||||
"bytemuck",
|
||||
|
@ -1210,7 +1210,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "spl-concurrent-merkle-tree"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"solana-program",
|
||||
|
@ -1232,9 +1232,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.96"
|
||||
version = "1.0.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
|
||||
checksum = "d56e159d99e6c2b93995d171050271edb50ecc5288fbc7cc17de8fdce4e58c14"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
@ -29,3 +29,8 @@ With a built local SDK, the test suite can be ran with:
|
|||
1. `yarn link @solana/spl-account-compression`
|
||||
2. `yarn`
|
||||
3. `yarn test`
|
||||
|
||||
## Audit
|
||||
|
||||
The repository [README](https://github.com/solana-labs/solana-program-library#audits)
|
||||
contains information about program audits.
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
[package]
|
||||
name = "spl-account-compression"
|
||||
version = "0.1.4"
|
||||
version = "0.1.10"
|
||||
description = "Solana Program Library Account Compression Program"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Labs Maintainers <maintainers@solanalabs.com>"]
|
||||
repository = "https://github.com/solana-labs/solana-program-library"
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
|
@ -18,7 +18,7 @@ cpi = ["no-entrypoint"]
|
|||
default = []
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = "0.25.0"
|
||||
anchor-lang = "0.26.0"
|
||||
bytemuck = "1.8.0"
|
||||
spl-concurrent-merkle-tree = { version="0.1.2", path="../../../libraries/concurrent-merkle-tree", features = [ "sol-log" ]}
|
||||
spl-noop = { version = "0.1.3", path="../noop", features = [ "no-entrypoint" ]}
|
||||
|
|
|
@ -19,12 +19,12 @@
|
|||
use crate::error::AccountCompressionError;
|
||||
use crate::events::ChangeLogEvent;
|
||||
use anchor_lang::prelude::*;
|
||||
use bytemuck::cast_slice_mut;
|
||||
use bytemuck::{cast_slice, cast_slice_mut};
|
||||
use spl_concurrent_merkle_tree::node::{empty_node_cached, Node, EMPTY};
|
||||
use std::mem::size_of;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn check_canopy_bytes(canopy_bytes: &mut [u8]) -> Result<()> {
|
||||
pub fn check_canopy_bytes(canopy_bytes: &[u8]) -> Result<()> {
|
||||
if canopy_bytes.len() % size_of::<Node>() != 0 {
|
||||
msg!(
|
||||
"Canopy byte length {} is not a multiple of {}",
|
||||
|
@ -38,7 +38,7 @@ pub fn check_canopy_bytes(canopy_bytes: &mut [u8]) -> Result<()> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn get_cached_path_length(canopy: &mut [Node], max_depth: u32) -> Result<u32> {
|
||||
fn get_cached_path_length(canopy: &[Node], max_depth: u32) -> Result<u32> {
|
||||
// The offset of 2 is applied because the canopy is a full binary tree without the root node
|
||||
// Size: (2^n - 2) -> Size + 2 must be a power of 2
|
||||
let closest_power_of_2 = (canopy.len() + 2) as u32;
|
||||
|
@ -89,7 +89,7 @@ pub fn update_canopy(
|
|||
}
|
||||
|
||||
pub fn fill_in_proof_from_canopy(
|
||||
canopy_bytes: &mut [u8],
|
||||
canopy_bytes: &[u8],
|
||||
max_depth: u32,
|
||||
index: u32,
|
||||
proof: &mut Vec<Node>,
|
||||
|
@ -97,7 +97,7 @@ pub fn fill_in_proof_from_canopy(
|
|||
// 30 is hard coded as it is the current max depth that SPL Compression supports
|
||||
let mut empty_node_cache = Box::new([EMPTY; 30]);
|
||||
check_canopy_bytes(canopy_bytes)?;
|
||||
let canopy = cast_slice_mut::<u8, Node>(canopy_bytes);
|
||||
let canopy = cast_slice::<u8, Node>(canopy_bytes);
|
||||
let path_len = get_cached_path_length(canopy, max_depth)?;
|
||||
|
||||
// We want to compute the node index (w.r.t. the canopy) where the current path
|
||||
|
@ -115,7 +115,6 @@ pub fn fill_in_proof_from_canopy(
|
|||
if canopy[cached_idx] == EMPTY {
|
||||
let level = max_depth - (31 - node_idx.leading_zeros());
|
||||
let empty_node = empty_node_cached::<30>(level, &mut empty_node_cache);
|
||||
canopy[cached_idx] = empty_node;
|
||||
inferred_nodes.push(empty_node);
|
||||
} else {
|
||||
inferred_nodes.push(canopy[cached_idx]);
|
||||
|
|
|
@ -27,11 +27,12 @@ use anchor_lang::{
|
|||
solana_program::sysvar::{clock::Clock, rent::Rent},
|
||||
};
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use std::mem::size_of;
|
||||
|
||||
pub mod canopy;
|
||||
pub mod error;
|
||||
pub mod events;
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
mod noop;
|
||||
pub mod state;
|
||||
pub mod zero_copy;
|
||||
|
@ -39,10 +40,12 @@ pub mod zero_copy;
|
|||
pub use crate::noop::{wrap_application_data_v1, Noop};
|
||||
|
||||
use crate::canopy::{fill_in_proof_from_canopy, update_canopy};
|
||||
use crate::error::AccountCompressionError;
|
||||
use crate::events::{AccountCompressionEvent, ChangeLogEvent};
|
||||
pub use crate::error::AccountCompressionError;
|
||||
pub use crate::events::{AccountCompressionEvent, ChangeLogEvent};
|
||||
use crate::noop::wrap_event;
|
||||
use crate::state::{ConcurrentMerkleTreeHeader, CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1};
|
||||
use crate::state::{
|
||||
merkle_tree_get_size, ConcurrentMerkleTreeHeader, CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1,
|
||||
};
|
||||
use crate::zero_copy::ZeroCopy;
|
||||
|
||||
/// Exported for Anchor / Solita
|
||||
|
@ -120,101 +123,6 @@ pub struct CloseTree<'info> {
|
|||
pub recipient: AccountInfo<'info>,
|
||||
}
|
||||
|
||||
/// This macro applies functions on a ConcurrentMerkleT:ee and emits leaf information
|
||||
/// needed to sync the merkle tree state with off-chain indexers.
|
||||
macro_rules! merkle_tree_depth_size_apply_fn {
|
||||
($max_depth:literal, $max_size:literal, $id:ident, $bytes:ident, $func:ident, $($arg:tt)*) => {
|
||||
match ConcurrentMerkleTree::<$max_depth, $max_size>::load_mut_bytes($bytes) {
|
||||
Ok(merkle_tree) => {
|
||||
match merkle_tree.$func($($arg)*) {
|
||||
Ok(_) => {
|
||||
Ok(Box::<ChangeLogEvent>::from((merkle_tree.get_change_log(), $id, merkle_tree.sequence_number)))
|
||||
}
|
||||
Err(err) => {
|
||||
msg!("Error using concurrent merkle tree: {}", err);
|
||||
err!(AccountCompressionError::ConcurrentMerkleTreeError)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
msg!("Error zero copying concurrent merkle tree: {}", err);
|
||||
err!(AccountCompressionError::ZeroCopyError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn merkle_tree_get_size(header: &ConcurrentMerkleTreeHeader) -> Result<usize> {
|
||||
// Note: max_buffer_size MUST be a power of 2
|
||||
match (header.get_max_depth(), header.get_max_buffer_size()) {
|
||||
(3, 8) => Ok(size_of::<ConcurrentMerkleTree<3, 8>>()),
|
||||
(5, 8) => Ok(size_of::<ConcurrentMerkleTree<5, 8>>()),
|
||||
(14, 64) => Ok(size_of::<ConcurrentMerkleTree<14, 64>>()),
|
||||
(14, 256) => Ok(size_of::<ConcurrentMerkleTree<14, 256>>()),
|
||||
(14, 1024) => Ok(size_of::<ConcurrentMerkleTree<14, 1024>>()),
|
||||
(14, 2048) => Ok(size_of::<ConcurrentMerkleTree<14, 2048>>()),
|
||||
(20, 64) => Ok(size_of::<ConcurrentMerkleTree<20, 64>>()),
|
||||
(20, 256) => Ok(size_of::<ConcurrentMerkleTree<20, 256>>()),
|
||||
(20, 1024) => Ok(size_of::<ConcurrentMerkleTree<20, 1024>>()),
|
||||
(20, 2048) => Ok(size_of::<ConcurrentMerkleTree<20, 2048>>()),
|
||||
(24, 64) => Ok(size_of::<ConcurrentMerkleTree<24, 64>>()),
|
||||
(24, 256) => Ok(size_of::<ConcurrentMerkleTree<24, 256>>()),
|
||||
(24, 512) => Ok(size_of::<ConcurrentMerkleTree<24, 512>>()),
|
||||
(24, 1024) => Ok(size_of::<ConcurrentMerkleTree<24, 1024>>()),
|
||||
(24, 2048) => Ok(size_of::<ConcurrentMerkleTree<24, 2048>>()),
|
||||
(26, 512) => Ok(size_of::<ConcurrentMerkleTree<26, 512>>()),
|
||||
(26, 1024) => Ok(size_of::<ConcurrentMerkleTree<26, 1024>>()),
|
||||
(26, 2048) => Ok(size_of::<ConcurrentMerkleTree<26, 2048>>()),
|
||||
(30, 512) => Ok(size_of::<ConcurrentMerkleTree<30, 512>>()),
|
||||
(30, 1024) => Ok(size_of::<ConcurrentMerkleTree<30, 1024>>()),
|
||||
(30, 2048) => Ok(size_of::<ConcurrentMerkleTree<30, 2048>>()),
|
||||
_ => {
|
||||
msg!(
|
||||
"Failed to get size of max depth {} and max buffer size {}",
|
||||
header.get_max_depth(),
|
||||
header.get_max_buffer_size()
|
||||
);
|
||||
err!(AccountCompressionError::ConcurrentMerkleTreeConstantsError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This applies a given function on a ConcurrentMerkleTree by
|
||||
/// allowing the compiler to infer the size of the tree based
|
||||
/// upon the header information stored on-chain
|
||||
macro_rules! merkle_tree_apply_fn {
|
||||
($header:ident, $id:ident, $bytes:ident, $func:ident, $($arg:tt)*) => {
|
||||
// Note: max_buffer_size MUST be a power of 2
|
||||
match ($header.get_max_depth(), $header.get_max_buffer_size()) {
|
||||
(3, 8) => merkle_tree_depth_size_apply_fn!(3, 8, $id, $bytes, $func, $($arg)*),
|
||||
(5, 8) => merkle_tree_depth_size_apply_fn!(5, 8, $id, $bytes, $func, $($arg)*),
|
||||
(14, 64) => merkle_tree_depth_size_apply_fn!(14, 64, $id, $bytes, $func, $($arg)*),
|
||||
(14, 256) => merkle_tree_depth_size_apply_fn!(14, 256, $id, $bytes, $func, $($arg)*),
|
||||
(14, 1024) => merkle_tree_depth_size_apply_fn!(14, 1024, $id, $bytes, $func, $($arg)*),
|
||||
(14, 2048) => merkle_tree_depth_size_apply_fn!(14, 2048, $id, $bytes, $func, $($arg)*),
|
||||
(20, 64) => merkle_tree_depth_size_apply_fn!(20, 64, $id, $bytes, $func, $($arg)*),
|
||||
(20, 256) => merkle_tree_depth_size_apply_fn!(20, 256, $id, $bytes, $func, $($arg)*),
|
||||
(20, 1024) => merkle_tree_depth_size_apply_fn!(20, 1024, $id, $bytes, $func, $($arg)*),
|
||||
(20, 2048) => merkle_tree_depth_size_apply_fn!(20, 2048, $id, $bytes, $func, $($arg)*),
|
||||
(24, 64) => merkle_tree_depth_size_apply_fn!(24, 64, $id, $bytes, $func, $($arg)*),
|
||||
(24, 256) => merkle_tree_depth_size_apply_fn!(24, 256, $id, $bytes, $func, $($arg)*),
|
||||
(24, 512) => merkle_tree_depth_size_apply_fn!(24, 512, $id, $bytes, $func, $($arg)*),
|
||||
(24, 1024) => merkle_tree_depth_size_apply_fn!(24, 1024, $id, $bytes, $func, $($arg)*),
|
||||
(24, 2048) => merkle_tree_depth_size_apply_fn!(24, 2048, $id, $bytes, $func, $($arg)*),
|
||||
(26, 512) => merkle_tree_depth_size_apply_fn!(26, 512, $id, $bytes, $func, $($arg)*),
|
||||
(26, 1024) => merkle_tree_depth_size_apply_fn!(26, 1024, $id, $bytes, $func, $($arg)*),
|
||||
(26, 2048) => merkle_tree_depth_size_apply_fn!(26, 2048, $id, $bytes, $func, $($arg)*),
|
||||
(30, 512) => merkle_tree_depth_size_apply_fn!(30, 512, $id, $bytes, $func, $($arg)*),
|
||||
(30, 1024) => merkle_tree_depth_size_apply_fn!(30, 1024, $id, $bytes, $func, $($arg)*),
|
||||
(30, 2048) => merkle_tree_depth_size_apply_fn!(30, 2048, $id, $bytes, $func, $($arg)*),
|
||||
_ => {
|
||||
msg!("Failed to apply {} on concurrent merkle tree with max depth {} and max buffer size {}", stringify!($func), $header.get_max_depth(), $header.get_max_buffer_size());
|
||||
err!(AccountCompressionError::ConcurrentMerkleTreeConstantsError)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[program]
|
||||
pub mod spl_account_compression {
|
||||
use super::*;
|
||||
|
@ -257,7 +165,7 @@ pub mod spl_account_compression {
|
|||
let merkle_tree_size = merkle_tree_get_size(&header)?;
|
||||
let (tree_bytes, canopy_bytes) = rest.split_at_mut(merkle_tree_size);
|
||||
let id = ctx.accounts.merkle_tree.key();
|
||||
let change_log_event = merkle_tree_apply_fn!(header, id, tree_bytes, initialize,)?;
|
||||
let change_log_event = merkle_tree_apply_fn_mut!(header, id, tree_bytes, initialize,)?;
|
||||
wrap_event(
|
||||
&AccountCompressionEvent::ChangeLog(*change_log_event),
|
||||
&ctx.accounts.noop,
|
||||
|
@ -362,7 +270,7 @@ pub mod spl_account_compression {
|
|||
fill_in_proof_from_canopy(canopy_bytes, header.get_max_depth(), index, &mut proof)?;
|
||||
let id = ctx.accounts.merkle_tree.key();
|
||||
// A call is made to ConcurrentMerkleTree::set_leaf(root, previous_leaf, new_leaf, proof, index)
|
||||
let change_log_event = merkle_tree_apply_fn!(
|
||||
let change_log_event = merkle_tree_apply_fn_mut!(
|
||||
header,
|
||||
id,
|
||||
tree_bytes,
|
||||
|
@ -421,16 +329,16 @@ pub mod spl_account_compression {
|
|||
crate::id(),
|
||||
AccountCompressionError::IncorrectAccountOwner
|
||||
);
|
||||
let mut merkle_tree_bytes = ctx.accounts.merkle_tree.try_borrow_mut_data()?;
|
||||
let merkle_tree_bytes = ctx.accounts.merkle_tree.try_borrow_data()?;
|
||||
let (header_bytes, rest) =
|
||||
merkle_tree_bytes.split_at_mut(CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1);
|
||||
merkle_tree_bytes.split_at(CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1);
|
||||
|
||||
let header = ConcurrentMerkleTreeHeader::try_from_slice(header_bytes)?;
|
||||
header.assert_valid()?;
|
||||
header.assert_valid_leaf_index(index)?;
|
||||
|
||||
let merkle_tree_size = merkle_tree_get_size(&header)?;
|
||||
let (tree_bytes, canopy_bytes) = rest.split_at_mut(merkle_tree_size);
|
||||
let (tree_bytes, canopy_bytes) = rest.split_at(merkle_tree_size);
|
||||
|
||||
let mut proof = vec![];
|
||||
for node in ctx.remaining_accounts.iter() {
|
||||
|
@ -465,7 +373,7 @@ pub mod spl_account_compression {
|
|||
let id = ctx.accounts.merkle_tree.key();
|
||||
let merkle_tree_size = merkle_tree_get_size(&header)?;
|
||||
let (tree_bytes, canopy_bytes) = rest.split_at_mut(merkle_tree_size);
|
||||
let change_log_event = merkle_tree_apply_fn!(header, id, tree_bytes, append, leaf)?;
|
||||
let change_log_event = merkle_tree_apply_fn_mut!(header, id, tree_bytes, append, leaf)?;
|
||||
update_canopy(
|
||||
canopy_bytes,
|
||||
header.get_max_depth(),
|
||||
|
@ -510,7 +418,7 @@ pub mod spl_account_compression {
|
|||
fill_in_proof_from_canopy(canopy_bytes, header.get_max_depth(), index, &mut proof)?;
|
||||
// A call is made to ConcurrentMerkleTree::fill_empty_or_append
|
||||
let id = ctx.accounts.merkle_tree.key();
|
||||
let change_log_event = merkle_tree_apply_fn!(
|
||||
let change_log_event = merkle_tree_apply_fn_mut!(
|
||||
header,
|
||||
id,
|
||||
tree_bytes,
|
||||
|
@ -548,7 +456,7 @@ pub mod spl_account_compression {
|
|||
let (tree_bytes, canopy_bytes) = rest.split_at_mut(merkle_tree_size);
|
||||
|
||||
let id = ctx.accounts.merkle_tree.key();
|
||||
merkle_tree_apply_fn!(header, id, tree_bytes, prove_tree_is_empty,)?;
|
||||
merkle_tree_apply_fn_mut!(header, id, tree_bytes, prove_tree_is_empty,)?;
|
||||
|
||||
// Close merkle tree account
|
||||
// 1. Move lamports
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
#[allow(dead_code)]
|
||||
enum TreeLoad {
|
||||
Immutable,
|
||||
Mutable,
|
||||
}
|
||||
|
||||
/// This macro applies functions on a ConcurrentMerkleT:ee and emits leaf information
|
||||
/// needed to sync the merkle tree state with off-chain indexers.
|
||||
#[macro_export]
|
||||
macro_rules! _merkle_tree_depth_size_apply_fn {
|
||||
($max_depth:literal, $max_size:literal, $id:ident, $bytes:ident, $func:ident, TreeLoad::Mutable, $($arg:tt)*)
|
||||
=> {
|
||||
match ConcurrentMerkleTree::<$max_depth, $max_size>::load_mut_bytes($bytes) {
|
||||
Ok(merkle_tree) => {
|
||||
match merkle_tree.$func($($arg)*) {
|
||||
Ok(_) => {
|
||||
Ok(Box::<ChangeLogEvent>::from((merkle_tree.get_change_log(), $id, merkle_tree.sequence_number)))
|
||||
}
|
||||
Err(err) => {
|
||||
msg!("Error using concurrent merkle tree: {}", err);
|
||||
err!(AccountCompressionError::ConcurrentMerkleTreeError)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
msg!("Error zero copying concurrent merkle tree: {}", err);
|
||||
err!(AccountCompressionError::ZeroCopyError)
|
||||
}
|
||||
}
|
||||
};
|
||||
($max_depth:literal, $max_size:literal, $id:ident, $bytes:ident, $func:ident, TreeLoad::Immutable, $($arg:tt)*) => {
|
||||
match ConcurrentMerkleTree::<$max_depth, $max_size>::load_bytes($bytes) {
|
||||
Ok(merkle_tree) => {
|
||||
match merkle_tree.$func($($arg)*) {
|
||||
Ok(_) => {
|
||||
Ok(Box::<ChangeLogEvent>::from((merkle_tree.get_change_log(), $id, merkle_tree.sequence_number)))
|
||||
}
|
||||
Err(err) => {
|
||||
msg!("Error using concurrent merkle tree: {}", err);
|
||||
err!(AccountCompressionError::ConcurrentMerkleTreeError)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
msg!("Error zero copying concurrent merkle tree: {}", err);
|
||||
err!(AccountCompressionError::ZeroCopyError)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// This applies a given function on a ConcurrentMerkleTree by
|
||||
/// allowing the compiler to infer the size of the tree based
|
||||
/// upon the header information stored on-chain
|
||||
#[macro_export]
|
||||
macro_rules! _merkle_tree_apply_fn {
|
||||
($header:ident, $($arg:tt)*) => {
|
||||
// Note: max_buffer_size MUST be a power of 2
|
||||
match ($header.get_max_depth(), $header.get_max_buffer_size()) {
|
||||
(3, 8) => _merkle_tree_depth_size_apply_fn!(3, 8, $($arg)*),
|
||||
(5, 8) => _merkle_tree_depth_size_apply_fn!(5, 8, $($arg)*),
|
||||
(14, 64) => _merkle_tree_depth_size_apply_fn!(14, 64, $($arg)*),
|
||||
(14, 256) => _merkle_tree_depth_size_apply_fn!(14, 256, $($arg)*),
|
||||
(14, 1024) => _merkle_tree_depth_size_apply_fn!(14, 1024, $($arg)*),
|
||||
(14, 2048) => _merkle_tree_depth_size_apply_fn!(14, 2048, $($arg)*),
|
||||
(15, 64) => _merkle_tree_depth_size_apply_fn!(15, 64, $($arg)*),
|
||||
(16, 64) => _merkle_tree_depth_size_apply_fn!(16, 64, $($arg)*),
|
||||
(17, 64) => _merkle_tree_depth_size_apply_fn!(17, 64, $($arg)*),
|
||||
(18, 64) => _merkle_tree_depth_size_apply_fn!(18, 64, $($arg)*),
|
||||
(19, 64) => _merkle_tree_depth_size_apply_fn!(19, 64, $($arg)*),
|
||||
(20, 64) => _merkle_tree_depth_size_apply_fn!(20, 64, $($arg)*),
|
||||
(20, 256) => _merkle_tree_depth_size_apply_fn!(20, 256, $($arg)*),
|
||||
(20, 1024) => _merkle_tree_depth_size_apply_fn!(20, 1024, $($arg)*),
|
||||
(20, 2048) => _merkle_tree_depth_size_apply_fn!(20, 2048, $($arg)*),
|
||||
(24, 64) => _merkle_tree_depth_size_apply_fn!(24, 64, $($arg)*),
|
||||
(24, 256) => _merkle_tree_depth_size_apply_fn!(24, 256, $($arg)*),
|
||||
(24, 512) => _merkle_tree_depth_size_apply_fn!(24, 512, $($arg)*),
|
||||
(24, 1024) => _merkle_tree_depth_size_apply_fn!(24, 1024, $($arg)*),
|
||||
(24, 2048) => _merkle_tree_depth_size_apply_fn!(24, 2048, $($arg)*),
|
||||
(26, 512) => _merkle_tree_depth_size_apply_fn!(26, 512, $($arg)*),
|
||||
(26, 1024) => _merkle_tree_depth_size_apply_fn!(26, 1024, $($arg)*),
|
||||
(26, 2048) => _merkle_tree_depth_size_apply_fn!(26, 2048, $($arg)*),
|
||||
(30, 512) => _merkle_tree_depth_size_apply_fn!(30, 512, $($arg)*),
|
||||
(30, 1024) => _merkle_tree_depth_size_apply_fn!(30, 1024, $($arg)*),
|
||||
(30, 2048) => _merkle_tree_depth_size_apply_fn!(30, 2048, $($arg)*),
|
||||
_ => {
|
||||
msg!("Failed to apply {} on concurrent merkle tree with max depth {} and max buffer size {}",
|
||||
stringify!($func),
|
||||
$header.get_max_depth(),
|
||||
$header.get_max_buffer_size()
|
||||
);
|
||||
err!(AccountCompressionError::ConcurrentMerkleTreeConstantsError)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// This applies a given function on a mutable ConcurrentMerkleTree
|
||||
#[macro_export]
|
||||
macro_rules! merkle_tree_apply_fn_mut {
|
||||
($header:ident, $id:ident, $bytes:ident, $func:ident, $($arg:tt)*) => {
|
||||
_merkle_tree_apply_fn!($header, $id, $bytes, $func, TreeLoad::Mutable, $($arg)*)
|
||||
};
|
||||
}
|
||||
|
||||
/// This applies a given function on a read-only ConcurrentMerkleTree
|
||||
#[macro_export]
|
||||
macro_rules! merkle_tree_apply_fn {
|
||||
($header:ident, $id:ident, $bytes:ident, $func:ident, $($arg:tt)*) => {
|
||||
_merkle_tree_apply_fn!($header, $id, $bytes, $func, TreeLoad::Immutable, $($arg)*)
|
||||
};
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
use anchor_lang::prelude::*;
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
|
||||
use spl_concurrent_merkle_tree::concurrent_merkle_tree::ConcurrentMerkleTree;
|
||||
use std::mem::size_of;
|
||||
|
||||
use crate::error::AccountCompressionError;
|
||||
|
||||
pub const CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1: usize = 2 + 54;
|
||||
|
@ -108,6 +111,12 @@ impl ConcurrentMerkleTreeHeader {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_creation_slot(&self) -> u64 {
|
||||
match &self.header {
|
||||
ConcurrentMerkleTreeHeaderData::V1(header) => header.creation_slot,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_new_authority(&mut self, new_authority: &Pubkey) {
|
||||
match self.header {
|
||||
ConcurrentMerkleTreeHeaderData::V1(ref mut header) => {
|
||||
|
@ -147,3 +156,43 @@ impl ConcurrentMerkleTreeHeader {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn merkle_tree_get_size(header: &ConcurrentMerkleTreeHeader) -> Result<usize> {
|
||||
// Note: max_buffer_size MUST be a power of 2
|
||||
match (header.get_max_depth(), header.get_max_buffer_size()) {
|
||||
(3, 8) => Ok(size_of::<ConcurrentMerkleTree<3, 8>>()),
|
||||
(5, 8) => Ok(size_of::<ConcurrentMerkleTree<5, 8>>()),
|
||||
(14, 64) => Ok(size_of::<ConcurrentMerkleTree<14, 64>>()),
|
||||
(14, 256) => Ok(size_of::<ConcurrentMerkleTree<14, 256>>()),
|
||||
(14, 1024) => Ok(size_of::<ConcurrentMerkleTree<14, 1024>>()),
|
||||
(14, 2048) => Ok(size_of::<ConcurrentMerkleTree<14, 2048>>()),
|
||||
(15, 64) => Ok(size_of::<ConcurrentMerkleTree<15, 64>>()),
|
||||
(16, 64) => Ok(size_of::<ConcurrentMerkleTree<16, 64>>()),
|
||||
(17, 64) => Ok(size_of::<ConcurrentMerkleTree<17, 64>>()),
|
||||
(18, 64) => Ok(size_of::<ConcurrentMerkleTree<18, 64>>()),
|
||||
(19, 64) => Ok(size_of::<ConcurrentMerkleTree<19, 64>>()),
|
||||
(20, 64) => Ok(size_of::<ConcurrentMerkleTree<20, 64>>()),
|
||||
(20, 256) => Ok(size_of::<ConcurrentMerkleTree<20, 256>>()),
|
||||
(20, 1024) => Ok(size_of::<ConcurrentMerkleTree<20, 1024>>()),
|
||||
(20, 2048) => Ok(size_of::<ConcurrentMerkleTree<20, 2048>>()),
|
||||
(24, 64) => Ok(size_of::<ConcurrentMerkleTree<24, 64>>()),
|
||||
(24, 256) => Ok(size_of::<ConcurrentMerkleTree<24, 256>>()),
|
||||
(24, 512) => Ok(size_of::<ConcurrentMerkleTree<24, 512>>()),
|
||||
(24, 1024) => Ok(size_of::<ConcurrentMerkleTree<24, 1024>>()),
|
||||
(24, 2048) => Ok(size_of::<ConcurrentMerkleTree<24, 2048>>()),
|
||||
(26, 512) => Ok(size_of::<ConcurrentMerkleTree<26, 512>>()),
|
||||
(26, 1024) => Ok(size_of::<ConcurrentMerkleTree<26, 1024>>()),
|
||||
(26, 2048) => Ok(size_of::<ConcurrentMerkleTree<26, 2048>>()),
|
||||
(30, 512) => Ok(size_of::<ConcurrentMerkleTree<30, 512>>()),
|
||||
(30, 1024) => Ok(size_of::<ConcurrentMerkleTree<30, 1024>>()),
|
||||
(30, 2048) => Ok(size_of::<ConcurrentMerkleTree<30, 2048>>()),
|
||||
_ => {
|
||||
msg!(
|
||||
"Failed to get size of max depth {} and max buffer size {}",
|
||||
header.get_max_depth(),
|
||||
header.get_max_buffer_size()
|
||||
);
|
||||
err!(AccountCompressionError::ConcurrentMerkleTreeConstantsError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,5 @@
|
|||
mod concurrent_merkle_tree_header;
|
||||
mod path_node;
|
||||
|
||||
pub use concurrent_merkle_tree_header::{
|
||||
ConcurrentMerkleTreeHeader, CONCURRENT_MERKLE_TREE_HEADER_SIZE_V1,
|
||||
};
|
||||
pub use concurrent_merkle_tree_header::*;
|
||||
pub use path_node::PathNode;
|
||||
|
|
|
@ -14,6 +14,15 @@ pub trait ZeroCopy: Pod {
|
|||
.map_err(error_msg::<Self>(data_len))
|
||||
.unwrap())
|
||||
}
|
||||
|
||||
fn load_bytes<'a>(data: &'a [u8]) -> Result<&'a Self> {
|
||||
let size = size_of::<Self>();
|
||||
let data_len = data.len();
|
||||
|
||||
Ok(bytemuck::try_from_bytes(&data[..size])
|
||||
.map_err(error_msg::<Self>(data_len))
|
||||
.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const MAX_DEPTH: usize, const MAX_BUFFER_SIZE: usize> ZeroCopy
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
name = "spl-noop"
|
||||
version = "0.1.3"
|
||||
description = "Solana Program Library No-op Program"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Labs Maintainers <maintainers@solanalabs.com>"]
|
||||
repository = "https://github.com/solana-labs/solana-program-library"
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
|
@ -16,4 +16,4 @@ cpi = ["no-entrypoint"]
|
|||
default = []
|
||||
|
||||
[dependencies]
|
||||
solana-program = "1.10.29"
|
||||
solana-program = "1.13.5"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"name": "@solana/spl-account-compression",
|
||||
"description": "SPL Account Compression Program JS API",
|
||||
"version": "0.1.4",
|
||||
"author": "Solana Maintainers <maintainers@solana.foundation>",
|
||||
"version": "0.1.8",
|
||||
"author": "Solana Labs Maintainers <maintainers@solanalabs.com>",
|
||||
"repository": {
|
||||
"url": "https://github.com/solana-labs/solana-program-library",
|
||||
"type": "git"
|
||||
|
@ -22,19 +22,18 @@
|
|||
"access": "public"
|
||||
},
|
||||
"main": "./dist/cjs/index.js",
|
||||
"module": "./dist/esm/index.js",
|
||||
"module": "./dist/cjs/index.js",
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"require": "./dist/cjs/index.js",
|
||||
"import": "./dist/esm/index.js",
|
||||
"types": "./dist/types/index.d.ts"
|
||||
"import": "./dist/cjs/index.js"
|
||||
},
|
||||
"./idl/spl_account_compression.json": "./idl/spl_account_compression.json",
|
||||
"./src/merkle-tree": "./src/merkle-tree/index.ts"
|
||||
"./idl/spl_account_compression.json": "./idl/spl_account_compression.json"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rm -rf dist/ && tsc && tsc -p tsconfig.cjs.json",
|
||||
"build": "rm -rf dist/ && tsc -p tsconfig.json",
|
||||
"fmt": "prettier --write '{*,**/*}.{ts,tsx,js,jsx,json}'",
|
||||
"pretty": "prettier --check '{,{src,test}/**/}*.{j,t}s'",
|
||||
"pretty:fix": "prettier --write '{,{src,test}/**/}*.{j,t}s'",
|
||||
|
@ -50,13 +49,16 @@
|
|||
"test:events": "start-server-and-test start-validator http://localhost:8899/health run-tests:events",
|
||||
"test:accounts": "start-server-and-test start-validator http://localhost:8899/health run-tests:accounts",
|
||||
"test:e2e": "start-server-and-test start-validator http://localhost:8899/health run-tests:e2e",
|
||||
"test:merkle-tree": "jest tests/merkleTree.test.ts --detectOpenHandles",
|
||||
"test": "start-server-and-test start-validator http://localhost:8899/health run-tests"
|
||||
},
|
||||
"dependencies": {
|
||||
"@metaplex-foundation/beet": "^0.7.1",
|
||||
"@metaplex-foundation/beet-solana": "^0.4.0",
|
||||
"bn.js": "^5.2.1",
|
||||
"borsh": "^0.7.0"
|
||||
"borsh": "^0.7.0",
|
||||
"js-sha3": "^0.8.0",
|
||||
"typescript-collections": "^1.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@solana/web3.js": "^1.50.1"
|
||||
|
@ -80,13 +82,11 @@
|
|||
"gh-pages": "^4.0.0",
|
||||
"jest": "^29.0.1",
|
||||
"jest-config": "^29.0.1",
|
||||
"js-sha3": "^0.8.0",
|
||||
"start-server-and-test": "^1.14.0",
|
||||
"ts-jest": "^28.0.8",
|
||||
"ts-jest-resolver": "^2.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typedoc": "^0.22.2",
|
||||
"typescript": "=4.7.4",
|
||||
"typescript-collections": "^1.3.3"
|
||||
"typescript": "=4.7.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { PublicKey } from '@solana/web3.js';
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
export const SPL_NOOP_ADDRESS = 'noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV';
|
||||
export const SPL_NOOP_ADDRESS = "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV";
|
||||
export const SPL_NOOP_PROGRAM_ID = new PublicKey(SPL_NOOP_ADDRESS);
|
||||
|
||||
/**
|
||||
|
@ -20,6 +20,11 @@ const allPairs: number[][] = [
|
|||
[14, 256],
|
||||
[14, 1024],
|
||||
[14, 2048],
|
||||
[15, 64],
|
||||
[16, 64],
|
||||
[17, 64],
|
||||
[18, 64],
|
||||
[19, 64],
|
||||
[20, 64],
|
||||
[20, 256],
|
||||
[20, 1024],
|
||||
|
@ -56,6 +61,11 @@ export type ValidDepthSizePair =
|
|||
| { maxDepth: 14; maxBufferSize: 256 }
|
||||
| { maxDepth: 14; maxBufferSize: 1024 }
|
||||
| { maxDepth: 14; maxBufferSize: 2048 }
|
||||
| { maxDepth: 15; maxBufferSize: 64 }
|
||||
| { maxDepth: 16; maxBufferSize: 64 }
|
||||
| { maxDepth: 17; maxBufferSize: 64 }
|
||||
| { maxDepth: 18; maxBufferSize: 64 }
|
||||
| { maxDepth: 19; maxBufferSize: 64 }
|
||||
| { maxDepth: 20; maxBufferSize: 64 }
|
||||
| { maxDepth: 20; maxBufferSize: 256 }
|
||||
| { maxDepth: 20; maxBufferSize: 1024 }
|
||||
|
|
|
@ -8,4 +8,5 @@ export * from './accounts';
|
|||
export * from './events';
|
||||
export * from './constants';
|
||||
export * from './types';
|
||||
export * from './merkle-tree';
|
||||
export type { ChangeLogEventV1 } from './types';
|
||||
|
|
|
@ -18,6 +18,12 @@ export class MerkleTree {
|
|||
root: Buffer;
|
||||
depth: number;
|
||||
|
||||
/**
|
||||
* Please use `MerkleTree.sparseMerkleTreeFromLeaves` to
|
||||
* create trees instead. This method is exposed for testing purposes,
|
||||
* and for those that are familiar with the MerkleTree data structure.
|
||||
* @param leaves leaf nodes of the tree
|
||||
*/
|
||||
constructor(leaves: Buffer[]) {
|
||||
let [nodes, finalLeaves] = buildLeaves(leaves);
|
||||
let seqNum = leaves.length;
|
||||
|
@ -53,6 +59,30 @@ export class MerkleTree {
|
|||
this.depth = nodes.peek()!.level + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the recommended way to create MerkleTrees.
|
||||
* If you're trying to match an on-chain MerkleTree,
|
||||
* set `depth` to `{@link ConcurrentMerkleTreeAccount}.getMaxDepth()`
|
||||
*
|
||||
* @param leaves leaves of the tree
|
||||
* @param depth number of levels in the tree
|
||||
* @returns MerkleTree
|
||||
*/
|
||||
static sparseMerkleTreeFromLeaves(
|
||||
leaves: Buffer[],
|
||||
depth: number
|
||||
): MerkleTree {
|
||||
const _leaves: Buffer[] = [];
|
||||
for (let i = 0; i < 2 ** depth; i++) {
|
||||
if (i < leaves.length) {
|
||||
_leaves.push(leaves[i]);
|
||||
} else {
|
||||
_leaves.push(Buffer.alloc(32));
|
||||
}
|
||||
}
|
||||
return new MerkleTree(_leaves);
|
||||
}
|
||||
|
||||
getRoot(): Buffer {
|
||||
return this.root;
|
||||
}
|
||||
|
@ -127,11 +157,10 @@ export class MerkleTree {
|
|||
this.root = node.node;
|
||||
}
|
||||
|
||||
verify(
|
||||
root: string,
|
||||
static hashProof(
|
||||
merkleTreeProof: MerkleTreeProof,
|
||||
verbose = false
|
||||
): boolean {
|
||||
verbose: boolean = false
|
||||
): Buffer {
|
||||
const { leaf, leafIndex, proof } = merkleTreeProof;
|
||||
|
||||
let node = new PublicKey(leaf).toBuffer();
|
||||
|
@ -143,12 +172,30 @@ export class MerkleTree {
|
|||
}
|
||||
if (verbose) console.log(`node ${i} ${new PublicKey(node).toString()}`);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that a root matches the proof.
|
||||
* @param root Root of a MerkleTree
|
||||
* @param merkleTreeProof Proof to a leaf in the MerkleTree
|
||||
* @param verbose Whether to print hashed nodes
|
||||
* @returns Whether the proof is valid
|
||||
*/
|
||||
static verify(
|
||||
root: Buffer,
|
||||
merkleTreeProof: MerkleTreeProof,
|
||||
verbose: boolean = false
|
||||
): boolean {
|
||||
const node = MerkleTree.hashProof(merkleTreeProof, verbose);
|
||||
const rehashed = new PublicKey(node).toString();
|
||||
const received = new PublicKey(root).toString();
|
||||
if (verbose) console.log(`hashed ${rehashed} got ${received}`);
|
||||
if (rehashed !== received) {
|
||||
throw new Error("Roots don't match!!!");
|
||||
if (verbose)
|
||||
console.log(`Roots don't match! Expected ${rehashed} got ${received}`);
|
||||
return false;
|
||||
}
|
||||
if (verbose) console.log(`Hashed ${rehashed} got ${received}`);
|
||||
return rehashed === received;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -436,48 +436,6 @@ describe('Account Compression', () => {
|
|||
cmt,
|
||||
);
|
||||
|
||||
assert(
|
||||
splCMT.getCurrentBufferIndex() === 0,
|
||||
"CMT updated its active index after attacker's transaction, when it shouldn't have done anything"
|
||||
);
|
||||
});
|
||||
it('Random attacker fails to fake the existence of a leaf by autocompleting proof', async () => {
|
||||
// As an attacker, we want to set `maliciousLeafHash1` by
|
||||
// providing `maliciousLeafHash` and `nodeProof` which hash to the current merkle tree root.
|
||||
// If we can do this, then we can set leaves to arbitrary values.
|
||||
const maliciousLeafHash = crypto.randomBytes(32);
|
||||
const maliciousLeafHash1 = crypto.randomBytes(32);
|
||||
const nodeProof: Buffer[] = [];
|
||||
for (let i = 0; i < DEPTH; i++) {
|
||||
nodeProof.push(Buffer.alloc(32));
|
||||
}
|
||||
|
||||
// Root - make this nonsense so it won't match what's in CL, and force proof autocompletion
|
||||
const replaceIx = createReplaceIx(
|
||||
cmt,
|
||||
payer,
|
||||
maliciousLeafHash1,
|
||||
{
|
||||
root: Buffer.alloc(32),
|
||||
leaf: maliciousLeafHash,
|
||||
leafIndex: 0,
|
||||
proof: nodeProof
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
await execute(provider, [replaceIx], [payerKeypair]);
|
||||
assert(
|
||||
false,
|
||||
'Attacker was able to succesfully write fake existence of a leaf'
|
||||
);
|
||||
} catch (e) { }
|
||||
|
||||
const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress(
|
||||
provider.connection,
|
||||
cmt,
|
||||
);
|
||||
|
||||
assert(
|
||||
splCMT.getCurrentBufferIndex() === 0,
|
||||
"CMT updated its active index after attacker's transaction, when it shouldn't have done anything"
|
||||
|
@ -486,6 +444,40 @@ describe('Account Compression', () => {
|
|||
});
|
||||
describe(`Canopy test`, () => {
|
||||
const DEPTH = 5;
|
||||
it(`Testing canopy for verify leaf instructions`, async () => {
|
||||
[cmtKeypair, offChainTree] = await createTreeOnChain(
|
||||
provider,
|
||||
payerKeypair,
|
||||
2 ** DEPTH,
|
||||
{ maxDepth: DEPTH, maxBufferSize: 8 },
|
||||
DEPTH // Store full tree on chain
|
||||
);
|
||||
cmt = cmtKeypair.publicKey;
|
||||
|
||||
const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress(
|
||||
connection,
|
||||
cmt,
|
||||
"confirmed"
|
||||
);
|
||||
let i = 0;
|
||||
const stepSize = 4;
|
||||
while (i < 2 ** DEPTH) {
|
||||
const ixs: TransactionInstruction[] = [];
|
||||
for (let j = 0; j < stepSize; j += 1) {
|
||||
const leafIndex = i + j;
|
||||
const leaf = offChainTree.leaves[leafIndex].node;
|
||||
const verifyIx = createVerifyLeafIx(cmt, {
|
||||
root: splCMT.getCurrentRoot(),
|
||||
leaf,
|
||||
leafIndex,
|
||||
proof: [],
|
||||
});
|
||||
ixs.push(verifyIx);
|
||||
}
|
||||
i += stepSize;
|
||||
await execute(provider, ixs, [payerKeypair]);
|
||||
}
|
||||
});
|
||||
it('Testing canopy for appends and replaces on a full on chain tree', async () => {
|
||||
[cmtKeypair, offChainTree] = await createTreeOnChain(
|
||||
provider,
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import { assert } from "chai";
|
||||
import * as crypto from "crypto";
|
||||
|
||||
import { emptyNode, MerkleTree } from "../src";
|
||||
|
||||
describe("MerkleTree tests", () => {
|
||||
it("Check constructor equivalence for depth 2 tree", () => {
|
||||
const leaves = [
|
||||
crypto.randomBytes(32),
|
||||
crypto.randomBytes(32),
|
||||
crypto.randomBytes(32),
|
||||
];
|
||||
const rawLeaves = leaves.concat(emptyNode(0));
|
||||
const merkleTreeRaw = new MerkleTree(rawLeaves);
|
||||
const merkleTreeSparse = MerkleTree.sparseMerkleTreeFromLeaves(leaves, 2);
|
||||
|
||||
assert(merkleTreeRaw.root.equals(merkleTreeSparse.root));
|
||||
});
|
||||
|
||||
const TEST_DEPTH = 14;
|
||||
it(`Check proofs for 2^${TEST_DEPTH} tree`, () => {
|
||||
const leaves: Buffer[] = [];
|
||||
for (let i = 0; i < 2 ** TEST_DEPTH; i++) {
|
||||
leaves.push(crypto.randomBytes(32));
|
||||
}
|
||||
const merkleTree = new MerkleTree(leaves);
|
||||
|
||||
// Check proofs
|
||||
for (let i = 0; i < leaves.length; i++) {
|
||||
const proof = merkleTree.getProof(i);
|
||||
assert(MerkleTree.verify(merkleTree.getRoot(), proof));
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"extends": "./tsconfig.base.json",
|
||||
"include": ["src"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/cjs/",
|
||||
"target": "ES2016",
|
||||
"module": "CommonJS",
|
||||
"sourceMap": true
|
||||
}
|
||||
}
|
|
@ -2,12 +2,12 @@
|
|||
"extends": "./tsconfig.base.json",
|
||||
"include": ["src"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist/esm/",
|
||||
"outDir": "dist/cjs/",
|
||||
"declarationDir": "dist/types",
|
||||
"module": "es2020",
|
||||
"target": "es2020",
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true
|
||||
"declarationMap": true,
|
||||
"target": "ES2016",
|
||||
"module": "CommonJS"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -781,9 +781,9 @@
|
|||
"@hapi/hoek" "^9.0.0"
|
||||
|
||||
"@sideway/formula@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c"
|
||||
integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f"
|
||||
integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==
|
||||
|
||||
"@sideway/pinpoint@^2.0.0":
|
||||
version "2.0.0"
|
||||
|
@ -3247,16 +3247,16 @@ json-stringify-safe@^5.0.1:
|
|||
integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
|
||||
|
||||
json5@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
|
||||
integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593"
|
||||
integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==
|
||||
dependencies:
|
||||
minimist "^1.2.0"
|
||||
|
||||
json5@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
|
||||
integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
|
||||
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
|
||||
|
||||
jsonc-parser@^3.0.0:
|
||||
version "3.2.0"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Labs Maintainers <maintainers@solanalabs.com>"]
|
||||
description = "SPL Associated Token Account Program Tests"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
name = "spl-associated-token-account-test"
|
||||
repository = "https://github.com/solana-labs/solana-program-library"
|
||||
|
@ -11,9 +11,9 @@ version = "0.0.1"
|
|||
test-sbf = []
|
||||
|
||||
[dev-dependencies]
|
||||
solana-program = "1.14.6"
|
||||
solana-program-test = "1.14.6"
|
||||
solana-sdk = "1.14.6"
|
||||
solana-program = "1.14.12"
|
||||
solana-program-test = "1.14.12"
|
||||
solana-sdk = "1.14.12"
|
||||
spl-associated-token-account = { version = "1.1", path = "../program", features = ["no-entrypoint"] }
|
||||
spl-token = { version = "3.5", path = "../../token/program", features = ["no-entrypoint"] }
|
||||
spl-token-2022 = { version = "0.5", path = "../../token/program-2022", features = ["no-entrypoint"] }
|
||||
spl-token-2022 = { version = "0.6", path = "../../token/program-2022", features = ["no-entrypoint"] }
|
||||
|
|
|
@ -28,8 +28,7 @@ use {
|
|||
async fn test_associated_token_account_with_transfer_fees() {
|
||||
let wallet_sender = Keypair::new();
|
||||
let wallet_address_sender = wallet_sender.pubkey();
|
||||
let wallet_receiver = Keypair::new();
|
||||
let wallet_address_receiver = wallet_receiver.pubkey();
|
||||
let wallet_address_receiver = Pubkey::new_unique();
|
||||
let (mut banks_client, payer, recent_blockhash) =
|
||||
program_test_2022(Pubkey::new_unique(), true).start().await;
|
||||
let rent = banks_client.get_rent().await.unwrap();
|
||||
|
@ -86,6 +85,11 @@ async fn test_associated_token_account_with_transfer_fees() {
|
|||
transaction.sign(&[&payer], recent_blockhash);
|
||||
banks_client.process_transaction(transaction).await.unwrap();
|
||||
|
||||
let recent_blockhash = banks_client
|
||||
.get_new_latest_blockhash(&recent_blockhash)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut transaction = Transaction::new_with_payer(
|
||||
&[create_associated_token_account(
|
||||
&payer.pubkey(),
|
||||
|
@ -156,6 +160,11 @@ async fn test_associated_token_account_with_transfer_fees() {
|
|||
)
|
||||
);
|
||||
|
||||
let recent_blockhash = banks_client
|
||||
.get_new_latest_blockhash(&recent_blockhash)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// success
|
||||
let transfer_amount = 500;
|
||||
let fee = 50;
|
||||
|
|
|
@ -175,6 +175,11 @@ async fn check_same_mint(context: &mut ProgramTestContext, program_id: &Pubkey)
|
|||
)
|
||||
.await;
|
||||
|
||||
context.last_blockhash = context
|
||||
.banks_client
|
||||
.get_new_latest_blockhash(&context.last_blockhash)
|
||||
.await
|
||||
.unwrap();
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[instruction::recover_nested(
|
||||
&wallet.pubkey(),
|
||||
|
@ -233,6 +238,11 @@ async fn check_different_mints(context: &mut ProgramTestContext, program_id: &Pu
|
|||
let destination_token_address =
|
||||
create_associated_token_account(context, &wallet.pubkey(), &nested_mint, program_id).await;
|
||||
|
||||
context.last_blockhash = context
|
||||
.banks_client
|
||||
.get_new_latest_blockhash(&context.last_blockhash)
|
||||
.await
|
||||
.unwrap();
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[instruction::recover_nested(
|
||||
&wallet.pubkey(),
|
||||
|
@ -291,6 +301,11 @@ async fn check_missing_wallet_signature(context: &mut ProgramTestContext, progra
|
|||
|
||||
let mut recover = instruction::recover_nested(&wallet.pubkey(), &mint, &mint, program_id);
|
||||
recover.accounts[5] = AccountMeta::new(wallet.pubkey(), false);
|
||||
context.last_blockhash = context
|
||||
.banks_client
|
||||
.get_new_latest_blockhash(&context.last_blockhash)
|
||||
.await
|
||||
.unwrap();
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[recover],
|
||||
Some(&context.payer.pubkey()),
|
||||
|
@ -342,6 +357,11 @@ async fn check_wrong_signer(context: &mut ProgramTestContext, program_id: &Pubke
|
|||
)
|
||||
.await;
|
||||
|
||||
context.last_blockhash = context
|
||||
.banks_client
|
||||
.get_new_latest_blockhash(&context.last_blockhash)
|
||||
.await
|
||||
.unwrap();
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[instruction::recover_nested(
|
||||
&wrong_wallet.pubkey(),
|
||||
|
@ -385,14 +405,19 @@ async fn fail_wrong_signer() {
|
|||
|
||||
async fn check_not_nested(context: &mut ProgramTestContext, program_id: &Pubkey) {
|
||||
let wallet = Keypair::new();
|
||||
let wrong_wallet = Keypair::new();
|
||||
let wrong_wallet = Pubkey::new_unique();
|
||||
let (mint, mint_authority) = create_mint(context, program_id).await;
|
||||
|
||||
let owner_associated_token_address =
|
||||
create_associated_token_account(context, &wallet.pubkey(), &mint, program_id).await;
|
||||
let nested_associated_token_address =
|
||||
create_associated_token_account(context, &wrong_wallet.pubkey(), &mint, program_id).await;
|
||||
create_associated_token_account(context, &wrong_wallet, &mint, program_id).await;
|
||||
|
||||
context.last_blockhash = context
|
||||
.banks_client
|
||||
.get_new_latest_blockhash(&context.last_blockhash)
|
||||
.await
|
||||
.unwrap();
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[instruction::recover_nested(
|
||||
&wallet.pubkey(),
|
||||
|
@ -439,7 +464,7 @@ async fn check_wrong_address_derivation_owner(
|
|||
program_id: &Pubkey,
|
||||
) {
|
||||
let wallet = Keypair::new();
|
||||
let wrong_wallet = Keypair::new();
|
||||
let wrong_wallet = Pubkey::new_unique();
|
||||
let (mint, mint_authority) = create_mint(context, program_id).await;
|
||||
|
||||
let owner_associated_token_address =
|
||||
|
@ -453,9 +478,14 @@ async fn check_wrong_address_derivation_owner(
|
|||
.await;
|
||||
|
||||
let wrong_owner_associated_token_address =
|
||||
get_associated_token_address_with_program_id(&mint, &wrong_wallet.pubkey(), program_id);
|
||||
get_associated_token_address_with_program_id(&mint, &wrong_wallet, program_id);
|
||||
let mut recover = instruction::recover_nested(&wallet.pubkey(), &mint, &mint, program_id);
|
||||
recover.accounts[3] = AccountMeta::new(wrong_owner_associated_token_address, false);
|
||||
context.last_blockhash = context
|
||||
.banks_client
|
||||
.get_new_latest_blockhash(&context.last_blockhash)
|
||||
.await
|
||||
.unwrap();
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[recover],
|
||||
Some(&context.payer.pubkey()),
|
||||
|
@ -506,6 +536,11 @@ async fn check_owner_account_does_not_exist(context: &mut ProgramTestContext, pr
|
|||
)
|
||||
.await;
|
||||
|
||||
context.last_blockhash = context
|
||||
.banks_client
|
||||
.get_new_latest_blockhash(&context.last_blockhash)
|
||||
.await
|
||||
.unwrap();
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[instruction::recover_nested(
|
||||
&wallet.pubkey(),
|
||||
|
@ -559,6 +594,11 @@ async fn fail_wrong_spl_token_program() {
|
|||
)
|
||||
.await;
|
||||
|
||||
context.last_blockhash = context
|
||||
.banks_client
|
||||
.get_new_latest_blockhash(&context.last_blockhash)
|
||||
.await
|
||||
.unwrap();
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[instruction::recover_nested(
|
||||
&wallet.pubkey(),
|
||||
|
@ -587,7 +627,7 @@ async fn fail_wrong_spl_token_program() {
|
|||
#[tokio::test]
|
||||
async fn fail_destination_not_wallet_ata() {
|
||||
let wallet = Keypair::new();
|
||||
let wrong_wallet = Keypair::new();
|
||||
let wrong_wallet = Pubkey::new_unique();
|
||||
let dummy_mint = Pubkey::new_unique();
|
||||
let pt = program_test_2022(dummy_mint, true);
|
||||
let program_id = spl_token_2022::id();
|
||||
|
@ -604,13 +644,17 @@ async fn fail_destination_not_wallet_ata() {
|
|||
)
|
||||
.await;
|
||||
let wrong_destination_associated_token_account_address =
|
||||
create_associated_token_account(&mut context, &wrong_wallet.pubkey(), &mint, &program_id)
|
||||
.await;
|
||||
create_associated_token_account(&mut context, &wrong_wallet, &mint, &program_id).await;
|
||||
|
||||
let mut recover = instruction::recover_nested(&wallet.pubkey(), &mint, &mint, &program_id);
|
||||
recover.accounts[2] =
|
||||
AccountMeta::new(wrong_destination_associated_token_account_address, false);
|
||||
|
||||
context.last_blockhash = context
|
||||
.banks_client
|
||||
.get_new_latest_blockhash(&context.last_blockhash)
|
||||
.await
|
||||
.unwrap();
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[recover],
|
||||
Some(&context.payer.pubkey()),
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
[package]
|
||||
name = "spl-associated-token-account"
|
||||
version = "1.1.1"
|
||||
version = "1.1.3"
|
||||
description = "Solana Program Library Associated Token Account"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Labs Maintainers <maintainers@solanalabs.com>"]
|
||||
repository = "https://github.com/solana-labs/solana-program-library"
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
no-entrypoint = []
|
||||
|
@ -16,9 +16,9 @@ assert_matches = "1.5.0"
|
|||
borsh = "0.9.1"
|
||||
num-derive = "0.3"
|
||||
num-traits = "0.2"
|
||||
solana-program = "1.14.6"
|
||||
solana-program = "1.14.12"
|
||||
spl-token = { version = "3.5", path = "../../token/program", features = ["no-entrypoint"] }
|
||||
spl-token-2022 = { version = "0.5", path = "../../token/program-2022", features = ["no-entrypoint"] }
|
||||
spl-token-2022 = { version = "0.6", path = "../../token/program-2022", features = ["no-entrypoint"] }
|
||||
thiserror = "1.0"
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -12,6 +12,11 @@ Now suppose the Bucks win Game 3, and the estimated probability of the Bucks win
|
|||
|
||||
We'll discuss this mechanism in more detail later.
|
||||
|
||||
## Audit
|
||||
|
||||
The repository [README](https://github.com/solana-labs/solana-program-library#audits)
|
||||
contains information about program audits.
|
||||
|
||||
## Client Setup
|
||||
First, clone down the repository (TODO publish to PyPI)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
attrs==21.2.0
|
||||
base58==2.1.0
|
||||
certifi==2021.5.30
|
||||
certifi==2022.12.7
|
||||
cffi==1.14.5
|
||||
chardet==4.0.0
|
||||
cheroot==8.5.2
|
||||
|
@ -15,7 +15,7 @@ jaraco.collections==3.3.0
|
|||
jaraco.functools==3.3.0
|
||||
jaraco.text==3.5.0
|
||||
more-itertools==8.8.0
|
||||
numpy==1.21.0
|
||||
numpy==1.22.0
|
||||
packaging==20.9
|
||||
pandas==1.3.0
|
||||
pluggy==0.13.1
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "binary-option"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
license = "WTFPL"
|
||||
|
||||
[features]
|
||||
|
@ -9,7 +9,7 @@ no-entrypoint = []
|
|||
test-sbf = []
|
||||
|
||||
[dependencies]
|
||||
solana-program = "1.14.6"
|
||||
solana-program = "1.14.12"
|
||||
thiserror = "1.0"
|
||||
spl-token = {version = "3.1.1", path = "../../token/program", features = ["no-entrypoint"]}
|
||||
arrayref = "0.3.6"
|
||||
|
|
|
@ -28,6 +28,8 @@ pub enum BinaryOptionError {
|
|||
PublicKeysShouldBeUnique,
|
||||
#[error("TradePricesIncorrect")]
|
||||
TradePricesIncorrect,
|
||||
#[error("AmountOverflow")]
|
||||
AmountOverflow,
|
||||
}
|
||||
|
||||
impl From<BinaryOptionError> for ProgramError {
|
||||
|
|
|
@ -234,7 +234,10 @@ pub fn process_trade(
|
|||
];
|
||||
|
||||
// Validate data
|
||||
if buy_price + sell_price != u64::pow(10, binary_option.decimals as u32) {
|
||||
let total_price = buy_price
|
||||
.checked_add(sell_price)
|
||||
.ok_or(BinaryOptionError::TradePricesIncorrect)?;
|
||||
if total_price != u64::pow(10, binary_option.decimals as u32) {
|
||||
return Err(BinaryOptionError::TradePricesIncorrect.into());
|
||||
}
|
||||
if binary_option.settled {
|
||||
|
@ -411,7 +414,7 @@ pub fn process_trade(
|
|||
seeds,
|
||||
)?;
|
||||
if n > n_b + n_s {
|
||||
binary_option.increment_supply(n - n_b - n_s);
|
||||
binary_option.increment_supply(n - n_b - n_s)?;
|
||||
} else {
|
||||
binary_option.decrement_supply(n - n_b - n_s)?;
|
||||
}
|
||||
|
@ -707,7 +710,10 @@ pub fn process_collect(program_id: &Pubkey, accounts: &[AccountInfo]) -> Program
|
|||
seeds,
|
||||
)?;
|
||||
if reward > 0 {
|
||||
let amount = (reward * escrow_account.amount) / binary_option.circulation;
|
||||
let amount = reward
|
||||
.checked_mul(escrow_account.amount)
|
||||
.ok_or(BinaryOptionError::AmountOverflow)?;
|
||||
let amount = amount / binary_option.circulation;
|
||||
spl_token_transfer_signed(
|
||||
token_program_info,
|
||||
escrow_account_info,
|
||||
|
|
|
@ -28,8 +28,12 @@ impl BinaryOption {
|
|||
Ok(binary_option)
|
||||
}
|
||||
|
||||
pub fn increment_supply(&mut self, n: u64) {
|
||||
self.circulation += n;
|
||||
pub fn increment_supply(&mut self, n: u64) -> ProgramResult {
|
||||
self.circulation = self
|
||||
.circulation
|
||||
.checked_add(n)
|
||||
.ok_or(BinaryOptionError::AmountOverflow)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn decrement_supply(&mut self, n: u64) -> ProgramResult {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Simple Oracle Pair Token
|
||||
# Simple Oracle Pair Token
|
||||
|
||||
1. pick a deposit token
|
||||
2. pick the decider's pubkey
|
||||
|
@ -10,3 +10,8 @@ the mint term end slot. After the decide term end slot the `Pass`
|
|||
token converts 1:1 with the deposit token if and only if the decider
|
||||
had set `pass` before the end of the decide term, otherwise the `Fail`
|
||||
token converts 1:1 with the deposit token.
|
||||
|
||||
## Audit
|
||||
|
||||
The repository [README](https://github.com/solana-labs/solana-program-library#audits)
|
||||
contains information about program audits.
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
name = "spl-binary-oracle-pair"
|
||||
version = "0.1.0"
|
||||
description = "Solana Program Library Binary Oracle Pair"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Labs Maintainers <maintainers@solanalabs.com>"]
|
||||
repository = "https://github.com/solana-labs/solana-program-library"
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
test-sbf = []
|
||||
|
@ -13,15 +13,15 @@ test-sbf = []
|
|||
[dependencies]
|
||||
num-derive = "0.3"
|
||||
num-traits = "0.2"
|
||||
solana-program = "1.14.6"
|
||||
solana-program = "1.14.12"
|
||||
spl-token = { version = "3.5", path = "../../token/program", features = [ "no-entrypoint" ] }
|
||||
thiserror = "1.0"
|
||||
uint = "0.9"
|
||||
borsh = "0.9.1"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-program-test = "1.14.6"
|
||||
solana-sdk = "1.14.6"
|
||||
solana-program-test = "1.14.12"
|
||||
solana-sdk = "1.14.12"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
|
|
|
@ -9,5 +9,11 @@ cargo_audit_ignores=(
|
|||
#
|
||||
# Blocked on chrono updating `time` to >= 0.2.23
|
||||
--ignore RUSTSEC-2020-0071
|
||||
|
||||
# tokio: vulnerability affecting named pipes on Windows
|
||||
#
|
||||
# Exception is a stopgap to unblock CI
|
||||
# https://github.com/solana-labs/solana/issues/29586
|
||||
--ignore RUSTSEC-2023-0001
|
||||
)
|
||||
cargo +"$rust_stable" audit "${cargo_audit_ignores[@]}"
|
||||
|
|
|
@ -10,3 +10,4 @@ cd name-service/js
|
|||
yarn install --pure-lockfile
|
||||
yarn lint
|
||||
yarn build
|
||||
yarn test
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
if [[ -n $SOLANA_VERSION ]]; then
|
||||
solana_version="$SOLANA_VERSION"
|
||||
else
|
||||
solana_version=v1.14.6
|
||||
solana_version=v1.14.12
|
||||
fi
|
||||
|
||||
export solana_version="$solana_version"
|
||||
|
|
|
@ -21,7 +21,7 @@ module.exports = {
|
|||
position: "left",
|
||||
},
|
||||
{
|
||||
href: "https://discordapp.com/invite/pquxPsq",
|
||||
href: "https://solana.com/discord",
|
||||
label: "Chat",
|
||||
position: "right",
|
||||
},
|
||||
|
@ -41,7 +41,7 @@ module.exports = {
|
|||
items: [
|
||||
{
|
||||
label: "Discord",
|
||||
href: "https://discordapp.com/invite/pquxPsq",
|
||||
href: "https://solana.com/discord",
|
||||
},
|
||||
{
|
||||
label: "Twitter",
|
||||
|
|
|
@ -3858,26 +3858,56 @@
|
|||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
|
||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz",
|
||||
"integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==",
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
||||
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/source-map": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz",
|
||||
"integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==",
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.11",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz",
|
||||
"integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg=="
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz",
|
||||
"integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==",
|
||||
"version": "0.3.18",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
|
||||
"integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
"@jridgewell/resolve-uri": "3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@mdx-js/mdx": {
|
||||
|
@ -4063,9 +4093,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@sideway/formula": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz",
|
||||
"integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg=="
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz",
|
||||
"integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg=="
|
||||
},
|
||||
"node_modules/@sideway/pinpoint": {
|
||||
"version": "2.0.0",
|
||||
|
@ -5020,9 +5050,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
||||
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
||||
"version": "2.6.4",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
||||
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.14"
|
||||
}
|
||||
|
@ -5098,12 +5128,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/babel-loader": {
|
||||
"version": "8.2.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz",
|
||||
"integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==",
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz",
|
||||
"integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==",
|
||||
"dependencies": {
|
||||
"find-cache-dir": "^3.3.1",
|
||||
"loader-utils": "^1.4.0",
|
||||
"loader-utils": "^2.0.0",
|
||||
"make-dir": "^3.1.0",
|
||||
"schema-utils": "^2.6.5"
|
||||
},
|
||||
|
@ -7867,19 +7897,6 @@
|
|||
"webpack": "^4.0.0 || ^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/file-loader/node_modules/loader-utils": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
|
||||
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
|
||||
"dependencies": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^3.0.0",
|
||||
"json5": "^2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/file-loader/node_modules/schema-utils": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
|
||||
|
@ -8775,9 +8792,9 @@
|
|||
"integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w=="
|
||||
},
|
||||
"node_modules/http-cache-semantics": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
|
||||
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
|
||||
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
|
||||
},
|
||||
"node_modules/http-deceiver": {
|
||||
"version": "1.2.7",
|
||||
|
@ -9586,12 +9603,9 @@
|
|||
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE="
|
||||
},
|
||||
"node_modules/json5": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
|
||||
"integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==",
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.5"
|
||||
},
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||
"bin": {
|
||||
"json5": "lib/cli.js"
|
||||
},
|
||||
|
@ -9707,27 +9721,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/loader-utils": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
|
||||
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
|
||||
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
|
||||
"dependencies": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^3.0.0",
|
||||
"json5": "^1.0.1"
|
||||
"json5": "^2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/loader-utils/node_modules/json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"json5": "lib/cli.js"
|
||||
"node": ">=8.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
|
@ -10108,19 +10111,6 @@
|
|||
"webpack": "^4.4.0 || ^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mini-css-extract-plugin/node_modules/loader-utils": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
|
||||
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
|
||||
"dependencies": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^3.0.0",
|
||||
"json5": "^2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mini-css-extract-plugin/node_modules/schema-utils": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
|
||||
|
@ -10155,9 +10145,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "0.3.0",
|
||||
|
@ -10264,9 +10257,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/node-forge": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz",
|
||||
"integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==",
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
|
||||
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
|
||||
"engines": {
|
||||
"node": ">= 6.13.0"
|
||||
}
|
||||
|
@ -12101,25 +12094,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/recursive-readdir": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
|
||||
"integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==",
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz",
|
||||
"integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==",
|
||||
"dependencies": {
|
||||
"minimatch": "3.0.4"
|
||||
"minimatch": "^3.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/recursive-readdir/node_modules/minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/regenerate": {
|
||||
|
@ -13034,31 +13016,20 @@
|
|||
}
|
||||
},
|
||||
"node_modules/serve-handler": {
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz",
|
||||
"integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==",
|
||||
"version": "6.1.5",
|
||||
"resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.5.tgz",
|
||||
"integrity": "sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==",
|
||||
"dependencies": {
|
||||
"bytes": "3.0.0",
|
||||
"content-disposition": "0.5.2",
|
||||
"fast-url-parser": "1.1.3",
|
||||
"mime-types": "2.1.18",
|
||||
"minimatch": "3.0.4",
|
||||
"minimatch": "3.1.2",
|
||||
"path-is-inside": "1.0.2",
|
||||
"path-to-regexp": "2.2.1",
|
||||
"range-parser": "1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-handler/node_modules/minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-index": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
|
||||
|
@ -13785,13 +13756,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.11.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.11.0.tgz",
|
||||
"integrity": "sha512-uCA9DLanzzWSsN1UirKwylhhRz3aKPInlfmpGfw8VN6jHsAtu8HJtIpeeHHK23rxnE/cDc+yvmq5wqkIC6Kn0A==",
|
||||
"version": "5.17.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.17.1.tgz",
|
||||
"integrity": "sha512-hVl35zClmpisy6oaoKALOpS0rDYLxRFLHhRuDlEGTKey9qHjS1w9GMORjuwIMt70Wan4lwsLYyWDVnWgF+KUEw==",
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
"acorn": "^8.5.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.7.2",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"bin": {
|
||||
|
@ -13875,14 +13846,6 @@
|
|||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
||||
},
|
||||
"node_modules/terser/node_modules/source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/text-table": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
||||
|
@ -14072,9 +14035,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/ua-parser-js": {
|
||||
"version": "0.7.31",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
|
||||
"integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==",
|
||||
"version": "0.7.35",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.35.tgz",
|
||||
"integrity": "sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
@ -14449,19 +14412,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/url-loader/node_modules/loader-utils": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
|
||||
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
|
||||
"dependencies": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^3.0.0",
|
||||
"json5": "^2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url-loader/node_modules/mime-db": {
|
||||
"version": "1.51.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
|
||||
|
@ -18239,23 +18189,47 @@
|
|||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
|
||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
|
||||
},
|
||||
"@jridgewell/gen-mapping": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
||||
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
|
||||
"requires": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"@jridgewell/resolve-uri": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz",
|
||||
"integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew=="
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
|
||||
},
|
||||
"@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw=="
|
||||
},
|
||||
"@jridgewell/source-map": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz",
|
||||
"integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==",
|
||||
"requires": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.11",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz",
|
||||
"integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg=="
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
|
||||
},
|
||||
"@jridgewell/trace-mapping": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz",
|
||||
"integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==",
|
||||
"version": "0.3.18",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
|
||||
"integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
|
||||
"requires": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
"@jridgewell/resolve-uri": "3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "1.4.14"
|
||||
}
|
||||
},
|
||||
"@mdx-js/mdx": {
|
||||
|
@ -18393,9 +18367,9 @@
|
|||
}
|
||||
},
|
||||
"@sideway/formula": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz",
|
||||
"integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg=="
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz",
|
||||
"integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg=="
|
||||
},
|
||||
"@sideway/pinpoint": {
|
||||
"version": "2.0.0",
|
||||
|
@ -19136,9 +19110,9 @@
|
|||
"integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ=="
|
||||
},
|
||||
"async": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
||||
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
||||
"version": "2.6.4",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
||||
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
|
||||
"requires": {
|
||||
"lodash": "^4.17.14"
|
||||
}
|
||||
|
@ -19191,12 +19165,12 @@
|
|||
}
|
||||
},
|
||||
"babel-loader": {
|
||||
"version": "8.2.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz",
|
||||
"integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==",
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz",
|
||||
"integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==",
|
||||
"requires": {
|
||||
"find-cache-dir": "^3.3.1",
|
||||
"loader-utils": "^1.4.0",
|
||||
"loader-utils": "^2.0.0",
|
||||
"make-dir": "^3.1.0",
|
||||
"schema-utils": "^2.6.5"
|
||||
}
|
||||
|
@ -21260,16 +21234,6 @@
|
|||
"schema-utils": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"loader-utils": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
|
||||
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^3.0.0",
|
||||
"json5": "^2.1.2"
|
||||
}
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
|
||||
|
@ -21936,9 +21900,9 @@
|
|||
}
|
||||
},
|
||||
"http-cache-semantics": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
|
||||
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
|
||||
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
|
||||
},
|
||||
"http-deceiver": {
|
||||
"version": "1.2.7",
|
||||
|
@ -22474,12 +22438,9 @@
|
|||
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE="
|
||||
},
|
||||
"json5": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
|
||||
"integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==",
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "6.1.0",
|
||||
|
@ -22560,23 +22521,13 @@
|
|||
"integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw=="
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
|
||||
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
|
||||
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^3.0.0",
|
||||
"json5": "^1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"requires": {
|
||||
"minimist": "^1.2.0"
|
||||
}
|
||||
}
|
||||
"json5": "^2.1.2"
|
||||
}
|
||||
},
|
||||
"locate-path": {
|
||||
|
@ -22875,16 +22826,6 @@
|
|||
"webpack-sources": "^1.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"loader-utils": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
|
||||
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^3.0.0",
|
||||
"json5": "^2.1.2"
|
||||
}
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
|
||||
|
@ -22911,9 +22852,9 @@
|
|||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.3.0",
|
||||
|
@ -22990,9 +22931,9 @@
|
|||
}
|
||||
},
|
||||
"node-forge": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz",
|
||||
"integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w=="
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
|
||||
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA=="
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "2.0.2",
|
||||
|
@ -24245,21 +24186,11 @@
|
|||
}
|
||||
},
|
||||
"recursive-readdir": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
|
||||
"integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==",
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz",
|
||||
"integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==",
|
||||
"requires": {
|
||||
"minimatch": "3.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
}
|
||||
"minimatch": "^3.0.5"
|
||||
}
|
||||
},
|
||||
"regenerate": {
|
||||
|
@ -24946,28 +24877,18 @@
|
|||
}
|
||||
},
|
||||
"serve-handler": {
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz",
|
||||
"integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==",
|
||||
"version": "6.1.5",
|
||||
"resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.5.tgz",
|
||||
"integrity": "sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==",
|
||||
"requires": {
|
||||
"bytes": "3.0.0",
|
||||
"content-disposition": "0.5.2",
|
||||
"fast-url-parser": "1.1.3",
|
||||
"mime-types": "2.1.18",
|
||||
"minimatch": "3.0.4",
|
||||
"minimatch": "3.1.2",
|
||||
"path-is-inside": "1.0.2",
|
||||
"path-to-regexp": "2.2.1",
|
||||
"range-parser": "1.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve-index": {
|
||||
|
@ -25515,13 +25436,13 @@
|
|||
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
|
||||
},
|
||||
"terser": {
|
||||
"version": "5.11.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.11.0.tgz",
|
||||
"integrity": "sha512-uCA9DLanzzWSsN1UirKwylhhRz3aKPInlfmpGfw8VN6jHsAtu8HJtIpeeHHK23rxnE/cDc+yvmq5wqkIC6Kn0A==",
|
||||
"version": "5.17.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.17.1.tgz",
|
||||
"integrity": "sha512-hVl35zClmpisy6oaoKALOpS0rDYLxRFLHhRuDlEGTKey9qHjS1w9GMORjuwIMt70Wan4lwsLYyWDVnWgF+KUEw==",
|
||||
"requires": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
"acorn": "^8.5.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.7.2",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -25534,11 +25455,6 @@
|
|||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -25710,9 +25626,9 @@
|
|||
"peer": true
|
||||
},
|
||||
"ua-parser-js": {
|
||||
"version": "0.7.31",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
|
||||
"integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ=="
|
||||
"version": "0.7.35",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.35.tgz",
|
||||
"integrity": "sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g=="
|
||||
},
|
||||
"unbox-primitive": {
|
||||
"version": "1.0.1",
|
||||
|
@ -25967,16 +25883,6 @@
|
|||
"schema-utils": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"loader-utils": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
|
||||
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^3.0.0",
|
||||
"json5": "^2.1.2"
|
||||
}
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.51.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
|
||||
|
|
|
@ -17,7 +17,7 @@ fi
|
|||
cat > "$CONFIG_FILE" <<EOF
|
||||
{
|
||||
"name": "$PROJECT_NAME",
|
||||
"scope": "solana-labs"
|
||||
"scope": "$VERCEL_SCOPE"
|
||||
}
|
||||
EOF
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ module.exports = {
|
|||
collapsed: true,
|
||||
items: [
|
||||
"token-2022",
|
||||
"token-2022/status",
|
||||
"token-2022/extensions",
|
||||
"token-2022/wallet",
|
||||
"token-2022/onchain",
|
||||
|
@ -28,6 +29,7 @@ module.exports = {
|
|||
"stake-pool",
|
||||
"stake-pool/quickstart",
|
||||
"stake-pool/overview",
|
||||
"stake-pool/fees",
|
||||
"stake-pool/cli",
|
||||
],
|
||||
},
|
||||
|
@ -51,5 +53,15 @@ module.exports = {
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Account Compression",
|
||||
collapsed: true,
|
||||
items: [
|
||||
"account-compression",
|
||||
"account-compression/concepts",
|
||||
"account-compression/usage",
|
||||
]
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
---
|
||||
title: Account Compression Program
|
||||
---
|
||||
|
||||
This on-chain program provides an interface for composing smart-contracts to create and use SPL ConcurrentMerkleTrees. The primary application of using SPL ConcurrentMerkleTrees is to make edits to off-chain data with on-chain verification.
|
||||
|
||||
## Motivation
|
||||
|
||||
- The high throughput of the Solana blockchain has increased the creation of Non fungible assets i.e NFTs due to their custodial ownership and censorship resistance characteristics. However, the practical use cases of these NFTs are limited by network storage costs when these are created at scale. It's rather inexpensive to mint a single non fungible token, however as you increase the quantity the cost of storing the asset's data on-chain becomes uneconomical.
|
||||
|
||||
- To fix this we must ensure the cost per token is as close to zero as possible. The solution is to store a compressed hash of the asset data on chain while maintaining the actual data off chain in a database. The program provides a way to verify the off chain data on chain and also make concurrent writes to the data. In order to do this we introduced a new data structure called a Concurrent Merkle Tree that avoids proof collision while making concurrent writes.
|
||||
|
||||
|
||||
## Background
|
||||
|
||||
The account compression program is currently being used for the [Metaplex Bubblegum Program](https://github.com/metaplex-foundation/metaplex-program-library/blob/master/bubblegum/)
|
||||
|
||||
To solve the problem of the high on-chain storage cost per unit of these assets, we need to store a compressed fingerprint on-chain that can verify the off-chain asset data. To do this we need
|
||||
- Concurrent Merkle Trees
|
||||
- The concurrent merkle trees allow us to compress all the data into a single root hash stored on-chain while allowing concurrent replacements and appends to the data.
|
||||
- Program indexer
|
||||
- The indexer is in charge of indexing the latest writes to the tree on chain so you know which nodes have been replaced and which have been appended to so you can avoid proof collision
|
||||
- Off-chain Database
|
||||
- The db stores the actual asset data off chain as we are only storing the merkle root on chain and we only need to be able to verify the data on chain.
|
||||
|
||||
The crux of this is the concurrent merkle tree and we shall learn about it in the next section.
|
||||
|
||||
## Source
|
||||
|
||||
The Account Compression Program's source is available on
|
||||
[github](https://github.com/solana-labs/solana-program-library).
|
||||
|
||||
|
||||
## Interface
|
||||
The Account Compression Program is written in rust and also has a typescript sdk for interacting with the program.
|
||||
|
||||
### Rust Packages
|
||||
| Name | Description | Program |
|
||||
| ---------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `spl-account-compression` | SDK for interacting with account compression program | [Rust Crate](https://crates.io/crates/spl-account-compression) and [Rust Docs](https://docs.rs/spl-account-compression) |
|
||||
| `spl-noop` | SDK for interacting with no op program, primarily for circumventing log truncation | [Rust Crate](https://crates.io/crates/spl-noop) and [Rust Docs](https://docs.rs/spl-noop) |
|
||||
| `spl-concurrent-merkle-tree` | SDK for creating SPL ConcurrentMerkleTrees | [Rust Crate](https://crates.io/crates/spl-concurrent-merkle-tree) and [Rust Docs](https://docs.rs/spl-concurrent-merkle-tree) |
|
||||
|
||||
### TypeScript Packages
|
||||
| Name | Description | Package |
|
||||
| --------------------------------- | ---------------------------------------------------- | -------------------------------------------------------------------- |
|
||||
| `@solana/spl-account-compression` | SDK for interacting with account compression program | [NPM](https://www.npmjs.com/package/@solana/spl-account-compression) |
|
||||
|
||||
## Testing and Development
|
||||
|
||||
Testing contracts locally requires the SDK to be built.
|
||||
|
||||
With a built local SDK, the test suite can be run with:
|
||||
|
||||
1. `yarn link @solana/spl-account-compression`
|
||||
2. `yarn`
|
||||
3. `yarn test`
|
|
@ -0,0 +1,73 @@
|
|||
---
|
||||
title: Core Concepts
|
||||
---
|
||||
|
||||
## Concurrent Merkle Trees
|
||||
To understand concurrent merkle trees we must first briefly understand merkle trees.
|
||||
|
||||
### Merkle Trees
|
||||
|
||||
A merkle tree is a hash based data structure that encodes data into a tree.
|
||||
The tree has nodes that are hashes of it's children and each leaf node is a hash of the data.
|
||||
|
||||
Each node has a 256 bit (32 byte) string represented by X<sub>i</sub> ∈ {0,1}^256 which is hashed using `H: {0, 1}^256 × {0, 1}^256 → {0, 1}^256`, meaning two child nodes with their 256 bit strings are hashed into one parent node with a 256 bit string. You can use any hash function that satisfies this property but we use SHA256.
|
||||
|
||||
Important properties of merkle trees:
|
||||
- The tree must be a fully balanced binary tree
|
||||
- Each Node *X<sub>i</sub> = H(X<sub>2i</sub>, X<sub>2i+1</sub>) for all i < 2^D*
|
||||
- Each Leaf Node *X<sub>i</sub> for all i <= 2^D*. X<sub>i</sub> is the hash of the data.
|
||||
|
||||
Because of these properties we can verify if certain data exists in tree while compressing all the data into a single 256 bit string called the root hash.
|
||||
|
||||
Example of a merkle tree of depth 2:
|
||||
```txt
|
||||
X1
|
||||
/ \
|
||||
X2 X3
|
||||
/ \ / \
|
||||
X4 X5 X6 X7
|
||||
```
|
||||
You can verify that X5 computes to X1 by doing X1 = H(H(X4,X5),X3)) where {X4,X5,X3} are the proof.
|
||||
If you change X5 to X5' then you will have to recompute the root hash in the following steps:
|
||||
- X2' = H(X4,X5')
|
||||
- X1' = H(X2',X3)
|
||||
|
||||
### Concurrent leaf replacement
|
||||
We know that there can be multiple concurrent requests to write to the same state, however when the root changes while the first write is happening the second write will generate an invalid root, in other words everytime a root is modified all modifications in progress will be invalid.
|
||||
```txt
|
||||
X1' X1''
|
||||
/ \ / \
|
||||
X2' X3 X2 X3''
|
||||
/ \ / \ / \ / \
|
||||
X4 X5' X6 X7 X4 X5 X6'' X7
|
||||
```
|
||||
In the above example let's say we try to modify `X5 -> X5'` and make another request to modify X6 -> X6''. For the first change we get root `X1'` computed using `X1' = H(H(X4,X5'),X3)`. For the second change we get root X1'' computed using `X1'' = H(H(X6'',X7),X2`). However `X1''` is not valid as `X1' != H(H(X6, X7), X2)` because the new root is actually `X1'`.
|
||||
|
||||
The reason this happens is because the change in the first trees path actualy changes the proofs required by the second trees change. To circumvent this problem we maintain a changelog of updates that have been made to the tree, so when `X5 -> X5'` the second mutation can actually use X2' instead of X2 which would compute to the correct root.
|
||||
|
||||
To swap the nodes when adding a new leaf in the second tree we do the following:
|
||||
- Take XOR of the leaf indices of the change log path and the new leaf in base 2
|
||||
- The depth at which you have to make the swap is the number of leading zeroes in the result(we also add one to it because the swap node is one below the intersection node)
|
||||
- At that depth change the node in the proof to the node in the changelog
|
||||
|
||||
Example with the previous trees:
|
||||
```txt
|
||||
2 1
|
||||
Changelog: [X5',X2']
|
||||
New Leaf: X6'' at leaf index 2
|
||||
|
||||
2 1
|
||||
Old proof for new leaf: [X7,X2]
|
||||
|
||||
1 XOR 2 = 001 XOR 010 = 011 (no leading zeroes)
|
||||
depth to swap at = 0 + 1 = 1
|
||||
|
||||
2 1
|
||||
New proof for new leaf: [X7,X2']
|
||||
```
|
||||
**Note:** We use XOR here because changelogs can get large as there can be many concurrent writes so using XOR is more efficient than a simple array search algorithm.
|
||||
|
||||
**Note**: Solana imposes a transactions size restriction of 1232 bytes hence the program also provides the ability to cache the upper most part of the concurrent merkle tree called a "canopy" which is stored at the end of the account.
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
---
|
||||
title: Example usage of the TS SDK
|
||||
---
|
||||
|
||||
|
||||
|
||||
## Install
|
||||
|
||||
```shell
|
||||
npm install --save @solana/spl-account-compression @solana/web3.js
|
||||
```
|
||||
|
||||
__OR__
|
||||
|
||||
```shell
|
||||
yarn add @solana/spl-account-compression @solana/web3.js
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
1. Create a tree
|
||||
|
||||
```typescript
|
||||
// Assume: known `payer` Keypair
|
||||
// Generate a keypair for the ConcurrentMerkleTree
|
||||
const cmtKeypair = Keypair.generate();
|
||||
// Create a system instruction to allocate enough
|
||||
// space for the tree
|
||||
const allocAccountIx = await createAllocTreeIx(
|
||||
connection,
|
||||
cmtKeypair.publicKey,
|
||||
payer.publicKey,
|
||||
{ maxDepth, maxBufferSize },
|
||||
canopyDepth,
|
||||
);
|
||||
// Create an SPL compression instruction to initialize
|
||||
// the newly created ConcurrentMerkleTree
|
||||
const initTreeIx = createInitEmptyMerkleTreeIx(
|
||||
cmtKeypair.publicKey,
|
||||
payer.publicKey,
|
||||
{ maxDepth, maxBufferSize }
|
||||
);
|
||||
const tx = new Transaction().add(allocAccountIx).add(initTreeIx);
|
||||
await sendAndConfirmTransaction(connection, tx, [cmtKeypair, payer]);
|
||||
```
|
||||
|
||||
2. Add a leaf to the tree
|
||||
|
||||
```typescript
|
||||
// Create a new leaf
|
||||
const newLeaf: Buffer = crypto.randomBytes(32);
|
||||
// Add the new leaf to the existing tree
|
||||
const appendIx = createAppendIx(cmtKeypair.publicKey, payer.publicKey, newLeaf);
|
||||
const tx = new Transaction().add(appendIx);
|
||||
await sendAndConfirmTransaction(connection, tx, [payer]);
|
||||
```
|
||||
|
||||
3. Replace a leaf in the tree, using the provided `MerkleTree` as an indexer
|
||||
|
||||
This example assumes that `offChainTree` has been indexing all previous modifying transactions
|
||||
involving this tree.
|
||||
It is okay for the indexer to be behind by a maximum of `maxBufferSize` transactions.
|
||||
|
||||
|
||||
```typescript
|
||||
// Assume: `offChainTree` is a MerkleTree instance
|
||||
// that has been indexing the `cmtKeypair.publicKey` transactions
|
||||
// Get a new leaf
|
||||
const newLeaf: Buffer = crypto.randomBytes(32);
|
||||
// Query off-chain records for information about the leaf
|
||||
// you wish to replace by its index in the tree
|
||||
const leafIndex = 314;
|
||||
// Replace the leaf at `leafIndex` with `newLeaf`
|
||||
const replaceIx = createReplaceIx(
|
||||
cmtKeypair.publicKey,
|
||||
payer.publicKey,
|
||||
newLeaf,
|
||||
offChainTree.getProof(leafIndex)
|
||||
);
|
||||
const tx = new Transaction().add(replaceIx);
|
||||
await sendAndConfirmTransaction(connection, tx, [payer]);
|
||||
```
|
||||
|
||||
4. Replace a leaf in the tree, using a 3rd party indexer
|
||||
|
||||
This example assumes that some 3rd party service is indexing the the tree at `cmtKeypair.publicKey` for you, and providing MerkleProofs via some REST endpoint.
|
||||
The `getProofFromAnIndexer` function is a **placeholder** to exemplify this relationship.
|
||||
|
||||
```typescript
|
||||
// Get a new leaf
|
||||
const newLeaf: Buffer = crypto.randomBytes(32);
|
||||
// Query off-chain indexer for a MerkleProof
|
||||
// possibly by executing GET request against a REST api
|
||||
const proof = await getProofFromAnIndexer(myOldLeaf);
|
||||
// Replace `myOldLeaf` with `newLeaf` at the same index in the tree
|
||||
const replaceIx = createReplaceIx(
|
||||
cmtKeypair.publicKey,
|
||||
payer.publicKey,
|
||||
newLeaf,
|
||||
proof
|
||||
);
|
||||
const tx = new Transaction().add(replaceIx);
|
||||
await sendAndConfirmTransaction(connection, tx, [payer]);
|
||||
```
|
||||
|
||||
## Reference examples
|
||||
|
||||
Here are some examples using account compression in the wild:
|
||||
|
||||
* Solana Program Library [tests](https://github.com/solana-labs/solana-program-library/tree/master/account-compression/sdk/tests)
|
||||
|
||||
* Metaplex Program Library Compressed NFT [tests](https://github.com/metaplex-foundation/metaplex-program-library/tree/master/bubblegum/js/tests)
|
|
@ -64,18 +64,18 @@ const SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID: PublicKey = new PublicKey(
|
|||
'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL',
|
||||
);
|
||||
|
||||
async function findAssociatedTokenAddress(
|
||||
function findAssociatedTokenAddress(
|
||||
walletAddress: PublicKey,
|
||||
tokenMintAddress: PublicKey
|
||||
): Promise<PublicKey> {
|
||||
return (await PublicKey.findProgramAddress(
|
||||
): PublicKey {
|
||||
return PublicKey.findProgramAddressSync(
|
||||
[
|
||||
walletAddress.toBuffer(),
|
||||
TOKEN_PROGRAM_ID.toBuffer(),
|
||||
tokenMintAddress.toBuffer(),
|
||||
],
|
||||
SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID
|
||||
))[0];
|
||||
)[0];
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -435,7 +435,7 @@ Account {
|
|||
A well-known limitation of using linearly-homomorphic ElGamal encryption is the
|
||||
inefficiency of decryption. Even with a proper secret key, in order to recover
|
||||
the originally encrypted value, one must solve a computational problem called
|
||||
the _discrete logarithm_, which requires an expoential time to solve. In the
|
||||
the _discrete logarithm_, which requires an exponential time to solve. In the
|
||||
confidential extension program, we address this issue in the following two ways:
|
||||
|
||||
- Transfer amounts are restricted to 48-bit numbers.
|
||||
|
|
Binary file not shown.
|
@ -187,9 +187,10 @@ amount. Specifically, a transaction fee is determined by two paramters:
|
|||
the fee rate of 1%, and `bp = 10000` represents the fee rate of 100%.
|
||||
|
||||
- `max_fee`: the max fee rate. A transfer fee is calculated using the fee rate
|
||||
that is determined by `bp`, but it is capped by `max_fee`.
|
||||
that is determined by `bp`, but it is capped by `max_fee`.
|
||||
|
||||
For example, consider a transfer amount of 200 tokens.
|
||||
|
||||
- For fee parameter `bp = 100` and `max_fee = 3`, the fee is simply 1% of the
|
||||
transfer amount, which is 2.
|
||||
- For fee parameter `bp = 200` and `max_fee = 3`, the fee is 3 since 2% of 200
|
||||
|
@ -221,7 +222,7 @@ The actual amount of a transfer fee cannot be included in the confidential
|
|||
extension `TransferWithFee` instruction in the clear since the transfer amount
|
||||
can be inferred from the fee. Therefore, in the confidential extension, the
|
||||
transfer fee is encrypted under the destination and withheld authority ElGamal
|
||||
public key.
|
||||
public key.
|
||||
|
||||
```rust
|
||||
struct FeeEncryption {
|
||||
|
@ -264,11 +265,22 @@ We refer to the proof specifications below for the additional details.
|
|||
|
||||
## Sigma Protocols
|
||||
|
||||
### Validity Proof
|
||||
### (Public-key) Validity Proof
|
||||
|
||||
A validity proof certifies that a twisted ElGamal ciphertext is a well-formed
|
||||
ciphertext. The precise description of the system is specified in the following
|
||||
notes.
|
||||
A public-key validity proof certifies that a twisted ElGamal public-key is a
|
||||
well-formed public key. The precise description of the system is specified in
|
||||
the following notes.
|
||||
|
||||
[[Notes]](./pubkey_proof.pdf)
|
||||
|
||||
The public-key validity proof is required for the `ConfigureAccount`
|
||||
instruction.
|
||||
|
||||
### (Ciphertext) Validity Proof
|
||||
|
||||
A ciphertext validity proof certifies that a twisted ElGamal ciphertext is a
|
||||
well-formed ciphertext. The precise description of the system is specified in
|
||||
the following notes.
|
||||
|
||||
[[Notes]](./validity_proof.pdf)
|
||||
|
||||
|
|
|
@ -37,10 +37,22 @@ chronological order, and the commit hash that each was reviewed at:
|
|||
* Quantstamp
|
||||
- Initial review commit hash [`99914c9`](https://github.com/solana-labs/solana-program-library/tree/99914c9fc7246b22ef04416586ab1722c89576de)
|
||||
- Re-review commit hash [`3b48fa0`](https://github.com/solana-labs/solana-program-library/tree/3b48fa09d38d1b66ffb4fef186b606f1bc4fdb31)
|
||||
- Final report https://solana.com/SolanaQuantstampStakePoolAudit.pdf
|
||||
- Final report https://github.com/solana-labs/security-audits/blob/master/spl/QuantstampStakePoolAudit-2021-10-22.pdf
|
||||
* Neodyme
|
||||
- Review commit hash [`0a85a9a`](https://github.com/solana-labs/solana-program-library/tree/0a85a9a533795b6338ea144e433893c6c0056210)
|
||||
- Report https://solana.com/SolanaNeodymeStakePoolAudit.pdf
|
||||
- Report https://github.com/solana-labs/security-audits/blob/master/spl/NeodymeStakePoolAudit-2021-10-16.pdf
|
||||
* Kudelski
|
||||
- Review commit hash [`3dd6767`](https://github.com/solana-labs/solana-program-library/tree/3dd67672974f92d3b648bb50ee74f4747a5f8973)
|
||||
- Report https://solana.com/SolanaKudelskiStakePoolAudit.pdf
|
||||
- Report https://github.com/solana-labs/security-audits/blob/master/spl/KudelskiStakePoolAudit-2021-07-07.pdf
|
||||
* Neodyme Second Audit
|
||||
- Review commit hash [`fd92ccf`](https://github.com/solana-labs/solana-program-library/tree/fd92ccf9e9308508b719d6e5f36474f57023b0b2)
|
||||
- Report https://github.com/solana-labs/security-audits/blob/master/spl/NeodymeStakePoolAudit-2022-12-10.pdf
|
||||
* OtterSec
|
||||
- Review commit hash [`eba709b`](https://github.com/solana-labs/solana-program-library/tree/eba709b9317f8c7b8b197045161cb744241f0bff)
|
||||
- Report https://github.com/solana-labs/security-audits/blob/master/spl/OtterSecStakePoolAudit-2023-01-20.pdf
|
||||
* Neodyme Third Audit
|
||||
- Review commit hash [`b341022`](https://github.com/solana-labs/solana-program-library/tree/b34102211f2a5ea6b83f3ee22f045fb115d87813)
|
||||
- Report https://github.com/solana-labs/security-audits/blob/master/spl/NeodymeStakePoolAudit-2023-01-31.pdf
|
||||
* Halborn
|
||||
- Review commit hash [`eba709b`](https://github.com/solana-labs/solana-program-library/tree/eba709b9317f8c7b8b197045161cb744241f0bff)
|
||||
- Report https://github.com/solana-labs/security-audits/blob/master/spl/HalbornStakePoolAudit-2023-01-25.pdf
|
||||
|
|
|
@ -169,18 +169,19 @@ Signature: 5yPXfVj5cbKBfZiEVi2UR5bXzVDuc2c3ruBwSjkAqpvxPHigwGHiS1mXQVE4qwok5moMW
|
|||
```
|
||||
|
||||
In order to protect stake pool depositors from malicious managers, the program
|
||||
applies the new fee for the following epoch.
|
||||
applies the new fee after crossing two epoch boundaries, giving a minimum wait
|
||||
time of one full epoch.
|
||||
|
||||
For example, if the fee is 1% at epoch 100, and the manager sets it to 10%, the
|
||||
manager will still gain 1% for the rewards earned during epoch 100. Starting
|
||||
with epoch 101, the manager will earn 10%.
|
||||
manager will still gain 1% for the rewards earned during epochs 100 and 101. Starting
|
||||
with epoch 102, the manager will earn 10%.
|
||||
|
||||
Additionally, to prevent a malicious manager from immediately setting the withdrawal
|
||||
fee to a very high amount, making it practically impossible for users to withdraw,
|
||||
the stake pool program currently enforces a limit of 1.5x increase per epoch.
|
||||
|
||||
For example, if the current withdrawal fee is 2.5%, the maximum that can be set
|
||||
for the next epoch is 3.75%.
|
||||
For example, if the current withdrawal fee is 2.5%, the maximum settable fee is
|
||||
3.75%, and will take effect after two epoch boundaries.
|
||||
|
||||
The possible options for the fee type are `epoch`, `sol-withdrawal`,
|
||||
`stake-withdrawal`, `sol-deposit`, and `stake-deposit`.
|
||||
|
|
|
@ -110,6 +110,7 @@ program, that creates new token accounts for either Token or Token-2022.
|
|||
To get started with Token-2022:
|
||||
|
||||
- [Install the Solana Tools](https://docs.solana.com/cli/install-solana-cli-tools)
|
||||
- [Project Status](token-2022/status.md)
|
||||
- [Extension Guide](token-2022/extensions.mdx)
|
||||
- [Wallet Guide](token-2022/wallet.md)
|
||||
- [On-Chain Program Guide](token-2022/onchain.md)
|
||||
|
@ -129,3 +130,18 @@ For information about the types and instructions, the Rust docs are available at
|
|||
|
||||
The Token-2022 Program is currently under multiple audits to ensure safety of
|
||||
funds. All audits will be published here as they are completed.
|
||||
|
||||
Here are the completed audits as of 3 April 2023:
|
||||
|
||||
* Halborn
|
||||
- Review commit hash [`c3137a`](https://github.com/solana-labs/solana-program-library/tree/c3137af9dfa2cc0873cc84c4418dea88ac542965/token/program-2022)
|
||||
- Final report https://github.com/solana-labs/security-audits/blob/master/spl/HalbornToken2022Audit-2022-07-27.pdf
|
||||
* Zellic
|
||||
- Review commit hash [`54695b`](https://github.com/solana-labs/solana-program-library/tree/54695b233484722458b18c0e26ebb8334f98422c/token/program-2022)
|
||||
- Final report https://github.com/solana-labs/security-audits/blob/master/spl/ZellicToken2022Audit-2022-12-05.pdf
|
||||
* Trail of Bits
|
||||
- Review commit hash [`50abad`](https://github.com/solana-labs/solana-program-library/tree/50abadd819df2e406567d6eca31c213264c1c7cd/token/program-2022)
|
||||
- Final report https://github.com/solana-labs/security-audits/blob/master/spl/TrailOfBitsToken2022Audit-2023-02-10.pdf
|
||||
* NCC Group
|
||||
- Review commit hash [`4e43aa`](https://github.com/solana-labs/solana/tree/4e43aa6c18e6bb4d98559f80eb004de18bc6b418/zk-token-sdk)
|
||||
- Final report https://github.com/solana-labs/security-audits/blob/master/spl/NCCToken2022Audit-2023-04-05.pdf
|
||||
|
|
|
@ -37,7 +37,10 @@ the `MintCloseAuthority` extension before initializing the mint.
|
|||
<Tabs className="unique-tabs" groupId="language-selection">
|
||||
<TabItem value="cli" label="CLI" default>
|
||||
|
||||
CLI support coming soon!
|
||||
```console
|
||||
$ spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token --enable-close
|
||||
Creating token C47NXhUTVEisCfX7s16KrxYyimnui7HpUXZecE2TmLdB under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jsx" label="JS">
|
||||
|
@ -112,7 +115,10 @@ possible to close the mint account and reclaim the lamports on the mint account.
|
|||
<Tabs className="unique-tabs" groupId="language-selection">
|
||||
<TabItem value="cli" label="CLI" default>
|
||||
|
||||
CLI support coming soon!
|
||||
```console
|
||||
$ spl-token close-mint C47NXhUTVEisCfX7s16KrxYyimnui7HpUXZecE2TmLdB
|
||||
Signature: 5nidwS9fJGJGdmaQjcwvNGVtk2ba5Zyu9ZLubjUKSsaAyzLUYvB6LK5RfUA767veBr45x7R1WW9N7WkYZ3Rqsb5B
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jsx" label="JS">
|
||||
|
@ -158,7 +164,15 @@ tokens.
|
|||
<Tabs className="unique-tabs" groupId="language-selection">
|
||||
<TabItem value="cli" label="CLI" default>
|
||||
|
||||
CLI support coming soon!
|
||||
```console
|
||||
$ spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token --transfer-fee 50 5000
|
||||
Creating token Dg3i18BN7vzsbAZDnDv3H8nQQjSaPUTqhwX41J7NZb5H under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
|
||||
|
||||
Address: Dg3i18BN7vzsbAZDnDv3H8nQQjSaPUTqhwX41J7NZb5H
|
||||
Decimals: 9
|
||||
|
||||
Signature: 39okFGqW23wQZ1HqH2tdJvtFP5aYgpfbmNktCZpV5XKTpKuA9xJmvBmrBwcLdfAT632VEC4y4dJJfDoeAvMWRPYP
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jsx" label="JS">
|
||||
|
@ -247,7 +261,29 @@ calculated, in order to avoid any surprises during the transfer.
|
|||
<Tabs className="unique-tabs" groupId="language-selection">
|
||||
<TabItem value="cli" label="CLI" default>
|
||||
|
||||
CLI support coming soon!
|
||||
```console
|
||||
$ spl-token create-account Dg3i18BN7vzsbAZDnDv3H8nQQjSaPUTqhwX41J7NZb5H
|
||||
Creating account 7UKuG4W68hW9eGrDms6BenRf8DCEHKGN49xewtWyB5cx
|
||||
|
||||
Signature: 6h591BMuguh9TtSdQPRPcPy97mLqJiybeaxGVZzD8mvPEsYypjZ2jjKgHzji5FGh8CJE3NAzqrqGxfyMdnbWrs7
|
||||
$ solana-keygen new -o destination.json
|
||||
$ spl-token create-account Dg3i18BN7vzsbAZDnDv3H8nQQjSaPUTqhwX41J7NZb5H destination.json
|
||||
Creating account 5wY8fiMZG5wGbQmtzKgqqEEp4vsCMJZ53RXEagUUWhEr
|
||||
|
||||
Signature: 2SyA17AJRWLH2j7svgxgW7nouUGioeWoRDWjz2Wq8j1eisThezSvqgN4NbHfj9uWmDh2XRp56ttZtHV1SxaUC7ys
|
||||
$ spl-token mint Dg3i18BN7vzsbAZDnDv3H8nQQjSaPUTqhwX41J7NZb5H 1000000000
|
||||
Minting 1000000000 tokens
|
||||
Token: Dg3i18BN7vzsbAZDnDv3H8nQQjSaPUTqhwX41J7NZb5H
|
||||
Recipient: 7UKuG4W68hW9eGrDms6BenRf8DCEHKGN49xewtWyB5cx
|
||||
|
||||
Signature: 5MFJGpLaWe3yLLU8X4ax3KofeqPVzdxJsa3ScjChJJHJawKsRx4og9eaFkWn3CPF7JXaxdj5v4LdAW56LiNTuP6s
|
||||
$ spl-token transfer --expected-fee 0.000005 Dg3i18BN7vzsbAZDnDv3H8nQQjSaPUTqhwX41J7NZb5H 1000000 destination.json
|
||||
Transfer 1000000 tokens
|
||||
Sender: 7UKuG4W68hW9eGrDms6BenRf8DCEHKGN49xewtWyB5cx
|
||||
Recipient: 5wY8fiMZG5wGbQmtzKgqqEEp4vsCMJZ53RXEagUUWhEr
|
||||
|
||||
Signature: 3hc3CCiETiuCArJ6yZ76ScyfMeK1rw8CTfZ3aDGnYoEMeoqXfSNAtnM3ATFjm7UihthzEkEWzeUfWL4qqqB4ofgv
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jsx" label="JS">
|
||||
|
@ -359,7 +395,10 @@ tokens.
|
|||
<Tabs className="unique-tabs" groupId="language-selection">
|
||||
<TabItem value="cli" label="CLI" default>
|
||||
|
||||
CLI support coming soon!
|
||||
```console
|
||||
$ spl-token withdraw-withheld-tokens 7UKuG4W68hW9eGrDms6BenRf8DCEHKGN49xewtWyB5cx 5wY8fiMZG5wGbQmtzKgqqEEp4vsCMJZ53RXEagUUWhEr
|
||||
Signature: 2NfjbEnRQC7kXkf86stb6u7eUtaQTGDebo8ktCdz4gP4wCD93xtx75rSJxJDQVePNAa8NqtVLjUm19ZBDRVaYurt
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jsx" label="JS">
|
||||
|
@ -397,7 +436,16 @@ To clear out their account of withheld tokens, they can use the permissionless
|
|||
<Tabs className="unique-tabs" groupId="language-selection">
|
||||
<TabItem value="cli" label="CLI" default>
|
||||
|
||||
CLI support coming soon!
|
||||
The harvest instruction isn't explicitly exposed since it typically isn't needed.
|
||||
It is required before closing an account, however, so we can show the harvest
|
||||
behavior by closing the account:
|
||||
|
||||
```console
|
||||
$ spl-token close --address 5wY8fiMZG5wGbQmtzKgqqEEp4vsCMJZ53RXEagUUWhEr
|
||||
Signature: KAKXryAdGSVFqpQhrwrvP6NCAQwLQp2Sj1WiAqCHxxwJsvRLKx4JzWgN9zYUaJNmfrZnQQw9yYoDw5Xx1YrwY6i
|
||||
|
||||
Signature: 2i5KGekFFtwzkX2W71cxPvQsGEH21qmZ3ieNQz7Mz2qGqp2pyzMNZhSVRfxJxQuAxnKQoZKjAb62FBx2gxaq25Le
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jsx" label="JS">
|
||||
|
@ -417,7 +465,11 @@ may choose to move those tokens from the mint to any other account.
|
|||
<Tabs className="unique-tabs" groupId="language-selection">
|
||||
<TabItem value="cli" label="CLI" default>
|
||||
|
||||
CLI support coming soon!
|
||||
```console
|
||||
$ spl-token withdraw-withheld-tokens --include-mint 7UKuG4W68hW9eGrDms6BenRf8DCEHKGN49xewtWyB5cx
|
||||
|
||||
Signature: 5KzdgcKgi3rLaBRfDbG5pxZwyKppyVjAA8TUCjTMfb1vMYv7CLQWaxgFz81jz4reUaF7oP67Gdqoc91Ted6qr1Hb
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jsx" label="JS">
|
||||
|
@ -456,7 +508,34 @@ tokens.
|
|||
<Tabs className="unique-tabs" groupId="language-selection">
|
||||
<TabItem value="cli" label="CLI" default>
|
||||
|
||||
CLI support coming soon!
|
||||
```console
|
||||
$ spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token --enable-freeze --default-account-state frozen
|
||||
Creating token 8Sqz2zV8TFTnkLtnCdqRkjJsre3GKRwHcZd3juE5jJHf under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
|
||||
|
||||
Address: 8Sqz2zV8TFTnkLtnCdqRkjJsre3GKRwHcZd3juE5jJHf
|
||||
Decimals: 9
|
||||
|
||||
Signature: 5wfYvovguPEbyv2uSWxGt9JcpTWgyuP4hY3wutjS32Ahnoni4qd7gf6sLre855WvT6xLHwrvV7J8bVmXymNU2qUz
|
||||
|
||||
$ spl-token create-account 8Sqz2zV8TFTnkLtnCdqRkjJsre3GKRwHcZd3juE5jJHf
|
||||
Creating account 6XpKagP1N3K1XnzStufpV5YZ6DksEkQWgLNG9kPpLyvv
|
||||
|
||||
Signature: 2awxWdQMgv89ew34sEyG361vshB2wPXHHfva5iJ43dWr18f2Pr6awoXfsqYPpyS2eSbH6jhfVY9EUck8iJ4wCSN6
|
||||
|
||||
$ spl-token display 6XpKagP1N3K1XnzStufpV5YZ6DksEkQWgLNG9kPpLyvv
|
||||
SPL Token Account
|
||||
Address: 6XpKagP1N3K1XnzStufpV5YZ6DksEkQWgLNG9kPpLyvv
|
||||
Program: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
|
||||
Balance: 0
|
||||
Decimals: 9
|
||||
Mint: 8Sqz2zV8TFTnkLtnCdqRkjJsre3GKRwHcZd3juE5jJHf
|
||||
Owner: 4SnSuUtJGKvk2GYpBwmEsWG53zTurVM8yXGsoiZQyMJn
|
||||
State: Frozen
|
||||
Delegation: (not set)
|
||||
Close authority: (not set)
|
||||
Extensions:
|
||||
Immutable owner
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jsx" label="JS">
|
||||
|
@ -534,7 +613,11 @@ accounts unfrozen by default.
|
|||
<Tabs className="unique-tabs" groupId="language-selection">
|
||||
<TabItem value="cli" label="CLI" default>
|
||||
|
||||
CLI support coming soon!
|
||||
```console
|
||||
$ spl-token update-default-account-state 8Sqz2zV8TFTnkLtnCdqRkjJsre3GKRwHcZd3juE5jJHf initialized
|
||||
|
||||
Signature: 3Mm2JCPrf6SrAe9awV3QzYvHiYmatiGWTmrQ7YnmzJSqyNCf75rLNMyH7jU26uZwX7q3MmBEBj1A36o5sGk9Vakb
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jsx" label="JS">
|
||||
|
@ -575,7 +658,20 @@ Account program always uses this extension when creating accounts.
|
|||
<Tabs className="unique-tabs" groupId="language-selection">
|
||||
<TabItem value="cli" label="CLI" default>
|
||||
|
||||
CLI support coming soon!
|
||||
```console
|
||||
$ spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token
|
||||
Creating token CZxztd7SEZWxg6B9PH5xa7QwKpMCpWBJiTLftw1o3qyV under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
|
||||
|
||||
Address: CZxztd7SEZWxg6B9PH5xa7QwKpMCpWBJiTLftw1o3qyV
|
||||
Decimals: 9
|
||||
|
||||
Signature: 4fT19YaE3zAscj71n213K22M3wDSXgwSn39RBCVtiCTxMX7pZhAoHywP2QMKqWpZMB5vT7diQ8QaFp3abHztpyPC
|
||||
$ solana-keygen new -o account.json
|
||||
$ spl-token create-account CZxztd7SEZWxg6B9PH5xa7QwKpMCpWBJiTLftw1o3qyV account.json --immutable
|
||||
Creating account EV2xsZto1TRqehewwWHUUQm68X6C6MepBSkbfZcVdShy
|
||||
|
||||
Signature: 5NqXiE3LPFnufnZhcwKPoZt7DaPR7qwfhmRr9W9ykhNM7rnu6MDdx7n5eTpEisiaSET2R4fZW7a91Ai6pCuskXF8
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jsx" label="JS">
|
||||
|
@ -652,7 +748,23 @@ it's extremely easy to use the extension.
|
|||
<Tabs className="unique-tabs" groupId="language-selection">
|
||||
<TabItem value="cli" label="CLI" default>
|
||||
|
||||
CLI support coming soon!
|
||||
```console
|
||||
$ spl-token create-account CZxztd7SEZWxg6B9PH5xa7QwKpMCpWBJiTLftw1o3qyV
|
||||
Creating account 4nvfLgYMERdNbbf1pADUSp44XukAyjeWWXCMkM1gMqC4
|
||||
|
||||
Signature: w4TRYDdCpTfmQh96E4UNgFFeiAHphWNaeYrJTu6bGyuPMokJrKFR33Ntj3iNQ5QQuFqom2CaYkhXiX9sBpWEW23
|
||||
```
|
||||
|
||||
The CLI will tell us that it's unnecessary to specify the `--immutable` argument
|
||||
if it's provided:
|
||||
|
||||
```console
|
||||
$ spl-token create-account CZxztd7SEZWxg6B9PH5xa7QwKpMCpWBJiTLftw1o3qyV --immutable
|
||||
Creating account 4nvfLgYMERdNbbf1pADUSp44XukAyjeWWXCMkM1gMqC4
|
||||
Note: --immutable specified, but Token-2022 ATAs are always immutable, ignoring
|
||||
|
||||
Signature: w4TRYDdCpTfmQh96E4UNgFFeiAHphWNaeYrJTu6bGyuPMokJrKFR33Ntj3iNQ5QQuFqom2CaYkhXiX9sBpWEW23
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jsx" label="JS">
|
||||
|
@ -687,7 +799,15 @@ but allows the owner to burn and close the account if they want.
|
|||
<Tabs className="unique-tabs" groupId="language-selection">
|
||||
<TabItem value="cli" label="CLI" default>
|
||||
|
||||
CLI support coming soon!
|
||||
```console
|
||||
$ spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token --enable-non-transferable
|
||||
Creating token 7De7wwkvNLPXpShbPDeRCLukb3CRzCNcC3iUuHtD6k4f under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
|
||||
|
||||
Address: 7De7wwkvNLPXpShbPDeRCLukb3CRzCNcC3iUuHtD6k4f
|
||||
Decimals: 9
|
||||
|
||||
Signature: 2QtCBwCo2J9hf2Prd2t4CBBUxEXQCBSSD5gkNc59AwhxsKgRp92czNAvwWDxjeXGFCWSuNmzAcD19cEpqubovDDv
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jsx" label="JS">
|
||||
|
@ -760,7 +880,21 @@ the memo before invoking the transfer.
|
|||
<Tabs className="unique-tabs" groupId="language-selection">
|
||||
<TabItem value="cli" label="CLI" default>
|
||||
|
||||
CLI support coming soon!
|
||||
```console
|
||||
$ spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token
|
||||
Creating token EbPBt3XkCb9trcV4c8fidhrvoeURbDbW87Acustzyi8N under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
|
||||
|
||||
Address: EbPBt3XkCb9trcV4c8fidhrvoeURbDbW87Acustzyi8N
|
||||
Decimals: 9
|
||||
|
||||
Signature: 2mCoV3ujSUArgZMyayiYtLZp2QzpqKx3NXnv9W8DpinY39rBU2yGmYLfp2tZ9uZqVbfJ6Mf3SqDHexdCcFcDAEvc
|
||||
$ spl-token create-account EbPBt3XkCb9trcV4c8fidhrvoeURbDbW87Acustzyi8N
|
||||
Creating account 4Uzz67txwYbfYpF8r5UGEMYJwhPAYQ5eFUY89KTYc2bL
|
||||
|
||||
Signature: 57wZHDaQtSzszDkusrnozZNj5PemQhpqHMEFLWFKpqASCErcDuBuYuEky5g3evHtkjMrKgh1s3aEap1L8y5UhW5W
|
||||
$ spl-token enable-required-transfer-memos 4Uzz67txwYbfYpF8r5UGEMYJwhPAYQ5eFUY89KTYc2bL
|
||||
Signature: 5MnWtrhMK32zkbacDMwBNft48VAUpr4EoRM87hkT9AFYvPgPEU7V7ERV6gdfb3kASri4wnUnr13hNKuYJ66pD8Fs
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jsx" label="JS">
|
||||
|
@ -839,7 +973,13 @@ An account owner may always choose to flip required memo transfers on or off.
|
|||
<Tabs className="unique-tabs" groupId="language-selection">
|
||||
<TabItem value="cli" label="CLI" default>
|
||||
|
||||
CLI support coming soon!
|
||||
```console
|
||||
$ spl-token disable-required-transfer-memos 4Uzz67txwYbfYpF8r5UGEMYJwhPAYQ5eFUY89KTYc2bL
|
||||
Signature: 5a9X8JrWzwZqb3iMonfUfSZbisQ57aEmW5cFntWGYRv2UZx8ACkMineBEQRHwLMzYHeyFDEHMXu8zqAMv5tm4u1g
|
||||
|
||||
$ spl-token enable-required-transfer-memos 4Uzz67txwYbfYpF8r5UGEMYJwhPAYQ5eFUY89KTYc2bL
|
||||
Signature: 5MnWtrhMK32zkbacDMwBNft48VAUpr4EoRM87hkT9AFYvPgPEU7V7ERV6gdfb3kASri4wnUnr13hNKuYJ66pD8Fs
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jsx" label="JS">
|
||||
|
@ -872,7 +1012,18 @@ to fit room for more extensions.
|
|||
<Tabs className="unique-tabs" groupId="language-selection">
|
||||
<TabItem value="cli" label="CLI" default>
|
||||
|
||||
CLI support coming soon!
|
||||
The CLI reallocs automatically, so if you use `enable-required-transfer-memos`
|
||||
with an account that does not have enough space, it will add the `Reallocate`
|
||||
instruction.
|
||||
|
||||
```console
|
||||
$ spl-token create-account EbPBt3XkCb9trcV4c8fidhrvoeURbDbW87Acustzyi8N
|
||||
Creating account 4Uzz67txwYbfYpF8r5UGEMYJwhPAYQ5eFUY89KTYc2bL
|
||||
|
||||
Signature: 57wZHDaQtSzszDkusrnozZNj5PemQhpqHMEFLWFKpqASCErcDuBuYuEky5g3evHtkjMrKgh1s3aEap1L8y5UhW5W
|
||||
$ spl-token enable-required-transfer-memos 4Uzz67txwYbfYpF8r5UGEMYJwhPAYQ5eFUY89KTYc2bL
|
||||
Signature: 5MnWtrhMK32zkbacDMwBNft48VAUpr4EoRM87hkT9AFYvPgPEU7V7ERV6gdfb3kASri4wnUnr13hNKuYJ66pD8Fs
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jsx" label="JS">
|
||||
|
@ -970,7 +1121,15 @@ plus all interest the tokens have accumulated. The feature is entirely cosmetic.
|
|||
<Tabs className="unique-tabs" groupId="language-selection">
|
||||
<TabItem value="cli" label="CLI" default>
|
||||
|
||||
CLI support coming soon!
|
||||
```console
|
||||
$ spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token --interest-rate 10
|
||||
Creating token 7N4HggYEJAtCLJdnHGCtFqfxcB5rhQCsQTze3ftYstVj under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
|
||||
|
||||
Address: 7N4HggYEJAtCLJdnHGCtFqfxcB5rhQCsQTze3ftYstVj
|
||||
Decimals: 9
|
||||
|
||||
Signature: 5dSW5QUacEsaKYb3MwYp4ycqq4jpNJ1rpLhS5rotoe3CWv9XhhjrncUFpk14R1fRamS1xprziC3NkpbYno4c8JxD
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jsx" label="JS">
|
||||
|
@ -1017,7 +1176,12 @@ The rate authority may update the interest rate on the mint at any time.
|
|||
<Tabs className="unique-tabs" groupId="language-selection">
|
||||
<TabItem value="cli" label="CLI" default>
|
||||
|
||||
CLI support coming soon!
|
||||
```console
|
||||
$ spl-token set-interest-rate 7N4HggYEJAtCLJdnHGCtFqfxcB5rhQCsQTze3ftYstVj 50
|
||||
Setting Interest Rate for 7N4HggYEJAtCLJdnHGCtFqfxcB5rhQCsQTze3ftYstVj to 50 bps
|
||||
|
||||
Signature: 5DQs6hzkfGq3uotESuVwF7MGeMawwfQcm1e9RHaUeVySDV6xpUzYhzdb6ygqJfsEZqewgiDR5KuxaGzkdTMcDrTn
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jsx" label="JS">
|
||||
|
@ -1038,3 +1202,108 @@ CLI support coming soon!
|
|||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Permanent Delegate
|
||||
|
||||
With Token-2022, it's possible to specify a permanent account delegate for a
|
||||
mint. This authority has unlimited delegate privileges over any account for that
|
||||
mint, meaning that it can burn or transfer any amount of tokens.
|
||||
|
||||
While this feature certainly has room for abuse, it has many important real-world
|
||||
use cases.
|
||||
|
||||
In some jurisdictions, a stablecoin issuer must be able to seize assets from
|
||||
sanctioned entities. Through the permanent delegate, the stablecoin issuer can
|
||||
transfer or burn tokens from accounts owned by sanctioned entities.
|
||||
|
||||
It's also possible to implement a [Harberger Tax](http://www.harbergertax.com/)
|
||||
on an NFT, whereby an auction program has permanent delegate authority for the
|
||||
token. After a sale, the permanent delegate can move the NFT from the owner to
|
||||
the buyer if the previous owner doesn't pay the tax.
|
||||
|
||||
#### Example: Create a mint with a permanent delegate
|
||||
|
||||
<Tabs className="unique-tabs" groupId="language-selection">
|
||||
<TabItem value="cli" label="CLI" default>
|
||||
|
||||
```console
|
||||
$ spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token --enable-permanent-delegate
|
||||
Creating token 7LUgoQCqhk3VMPhpAnmS1zdCFW4C6cupxgbqWrTwydGx under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
|
||||
|
||||
Address: 7LUgoQCqhk3VMPhpAnmS1zdCFW4C6cupxgbqWrTwydGx
|
||||
Decimals: 9
|
||||
|
||||
Signature: 439yVq2WfUEegAPv5BAkFampBPo696UbZ58RAYCzvUcbcBcxhfThpt1pcdKmiQrurHj65CqmWiHzrfT12BhL3Nxb
|
||||
```
|
||||
|
||||
The CLI defaults the permanent delegate to the mint authority, but you can change
|
||||
it using the `authorize` command.
|
||||
|
||||
```console
|
||||
$ spl-token authorize 7LUgoQCqhk3VMPhpAnmS1zdCFW4C6cupxgbqWrTwydGx permanent-delegate GFMniFoE5X4F87L9jzjHaW4MTkXyX1AYHNfhFencgamg
|
||||
Updating 7LUgoQCqhk3VMPhpAnmS1zdCFW4C6cupxgbqWrTwydGx
|
||||
Current permanent delegate: 4SnSuUtJGKvk2GYpBwmEsWG53zTurVM8yXGsoiZQyMJn
|
||||
New permanent delegate: GFMniFoE5X4F87L9jzjHaW4MTkXyX1AYHNfhFencgamg
|
||||
|
||||
Signature: 2ABDrR6meXk4rrAwd2LsHaTsnM5BuTC9RbiZmgBxgzze8ZM2yxuYp8iyg8viHgVaKRbXGzjKsFjF5RR9Kkzn4Prj
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jsx" label="JS">
|
||||
|
||||
```jsx
|
||||
import {
|
||||
clusterApiUrl,
|
||||
sendAndConfirmTransaction,
|
||||
Connection,
|
||||
Keypair,
|
||||
SystemProgram,
|
||||
Transaction,
|
||||
LAMPORTS_PER_SOL,
|
||||
} from '@solana/web3.js';
|
||||
|
||||
import {
|
||||
ExtensionType,
|
||||
createInitializeMintInstruction,
|
||||
createInitializePermanentDelegateInstruction,
|
||||
mintTo,
|
||||
createAccount,
|
||||
getMintLen,
|
||||
TOKEN_2022_PROGRAM_ID,
|
||||
} from '../src';
|
||||
|
||||
(async () => {
|
||||
const payer = Keypair.generate();
|
||||
|
||||
const mintAuthority = Keypair.generate();
|
||||
const mintKeypair = Keypair.generate();
|
||||
const mint = mintKeypair.publicKey;
|
||||
const permanentDelegate = Keypair.generate();
|
||||
|
||||
const extensions = [ExtensionType.PermanentDelegate];
|
||||
const mintLen = getMintLen(extensions);
|
||||
const decimals = 9;
|
||||
|
||||
const connection = new Connection(clusterApiUrl('devnet'), 'confirmed');
|
||||
|
||||
const airdropSignature = await connection.requestAirdrop(payer.publicKey, 2 * LAMPORTS_PER_SOL);
|
||||
await connection.confirmTransaction({ signature: airdropSignature, ...(await connection.getLatestBlockhash()) });
|
||||
|
||||
const mintLamports = await connection.getMinimumBalanceForRentExemption(mintLen);
|
||||
const mintTransaction = new Transaction().add(
|
||||
SystemProgram.createAccount({
|
||||
fromPubkey: payer.publicKey,
|
||||
newAccountPubkey: mint,
|
||||
space: mintLen,
|
||||
lamports: mintLamports,
|
||||
programId: TOKEN_2022_PROGRAM_ID,
|
||||
}),
|
||||
createInitializePermanentDelegateInstruction(mint, permanentDelegate.publicKey, TOKEN_2022_PROGRAM_ID),
|
||||
createInitializeMintInstruction(mint, decimals, mintAuthority.publicKey, null, TOKEN_2022_PROGRAM_ID)
|
||||
);
|
||||
await sendAndConfirmTransaction(connection, mintTransaction, [payer, mintKeypair], undefined);
|
||||
})();
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
---
|
||||
title: Project Status
|
||||
---
|
||||
|
||||
The Token-2022 program is still under audit and not meant for full production use.
|
||||
In the meantime, all clusters have the latest program deployed **for testing and
|
||||
development purposes ONLY**.
|
||||
|
||||
## Timeline
|
||||
|
||||
Here is the general program timeline and rough ETAs:
|
||||
|
||||
| Issue | ETA |
|
||||
| --- | --- |
|
||||
| Finish program | end of May |
|
||||
| Final audit | end of June |
|
||||
| Mainnet recommendation | middle of July |
|
||||
| Freeze program | Q1 2024 |
|
||||
|
||||
More information: https://github.com/orgs/solana-labs/projects/34
|
||||
|
||||
## Remaining items
|
||||
|
||||
### v1.14 with curve syscalls
|
||||
|
||||
In order to use confidential tokens, the cluster must run at least version 1.14
|
||||
with the elliptic curve operations syscalls enabled.
|
||||
|
||||
More information: https://github.com/solana-labs/solana/issues/29612
|
||||
|
||||
### Zero-knowledge proof split
|
||||
|
||||
To fit within the current transaction size limits, the zero knowledge proofs must
|
||||
be split into their component parts and uploaded to the chain through multiple
|
||||
transactions. Once the proofs exist, the user can issue a transfer and clean up
|
||||
the proofs.
|
||||
|
||||
After splitting the proofs in the zero-knowledge token SDK, the token-2022 program
|
||||
must properly consume the new proof format.
|
||||
|
||||
More information: https://github.com/solana-labs/solana/pull/30816
|
||||
|
||||
### Permissioned-transfer extension
|
||||
|
||||
There are many mutually exclusive solutions for restricting transfer of tokens,
|
||||
that all require different sets of accounts to validate a transfer.
|
||||
|
||||
The permissioned-transfer extension allows a mint creator to specify a program
|
||||
that must be called to validate transfers. The program must conform to the
|
||||
permissioned-transfer program interface.
|
||||
|
||||
More information: https://github.com/solana-labs/solana-program-library/pull/4105
|
||||
|
||||
## Future work
|
||||
|
||||
### Wallets
|
||||
|
||||
To start, wallets need to properly handle the token-2022 program and its accounts,
|
||||
by fetching token-2022 accounts and sending instructions to the proper program.
|
||||
|
||||
Next, to use confidential tokens, wallets need to create zero-knowledge proofs,
|
||||
which entails a new transaction flow.
|
||||
|
||||
### Increased transaction size
|
||||
|
||||
To support confidential transfers in one transaction, rather than split up over
|
||||
multiple transactions, the Solana network must accept transactions with a larger
|
||||
payload.
|
||||
|
||||
More information: https://github.com/orgs/solana-labs/projects/16
|
||||
|
||||
## Upgradability
|
||||
|
||||
To facilitate deploying updates and security fixes, the program deployment remains
|
||||
upgradable. Once audits are complete and the program has been stable for six months,
|
||||
the deployment will be marked final and no further upgrades will be possible.
|
|
@ -2,13 +2,10 @@
|
|||
title: Token-Lending Program
|
||||
---
|
||||
|
||||
A lending protocol for the Token program on the Solana blockchain inspired by Aave and Compound.
|
||||
A lending protocol for the Token program on the Solana blockchain inspired by
|
||||
Aave and Compound.
|
||||
|
||||
## Audit
|
||||
|
||||
### On-Chain Programs
|
||||
|
||||
| Cluster | Program Address |
|
||||
| --- | --- |
|
||||
| Mainnet Beta | [`LendZqTs8gn5CTSJU1jWKhKuVpjJGom45nnwPb2AMTi`](https://explorer.solana.com/address/LendZqTs7gn5CTSJU1jWKhKuVpjJGom45nnwPb2AMTi) |
|
||||
| Testnet | [`LendZqTs8gn5CTSJU1jWKhKuVpjJGom45nnwPb2AMTi`](https://explorer.solana.com/address/LendZqTs8gn5CTSJU1jWKhKuVpjJGom45nnwPb2AMTi?cluster=testnet) |
|
||||
| Devnet | [`LendZqTs8gn5CTSJU1jWKhKuVpjJGom45nnwPb2AMTi`](https://explorer.solana.com/address/LendZqTs8gn5CTSJU1jWKhKuVpjJGom45nnwPb2AMTi?cluster=devnet) |
|
||||
The repository [README](https://github.com/solana-labs/solana-program-library#audits)
|
||||
contains information about program audits.
|
||||
|
|
|
@ -5,25 +5,22 @@ title: Token Swap Program
|
|||
A Uniswap-like exchange for the Token program on the Solana blockchain,
|
||||
implementing multiple automated market maker (AMM) curves.
|
||||
|
||||
## Audit
|
||||
|
||||
The repository [README](https://github.com/solana-labs/solana-program-library#audits)
|
||||
contains information about program audits.
|
||||
|
||||
## Available Deployments
|
||||
|
||||
|
||||
| Network | Version | Program Address | Fee Owner Address |
|
||||
| Network | Version | Program Address |
|
||||
| --- | --- | --- |
|
||||
| Devnet, Testnet | 3.0.0 | `SwapsVeCiPHMUAtzQWZw7RjsKjgCjhwU55QGu4U1Szw` | Any |
|
||||
| All | 2.0.0 | `SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8` | `HfoTxFR1Tm6kGmWgYWD6J7YHVy1UwqSULUGVLXkJqaKN` |
|
||||
| Testnet | 3.0.0 | `SwapsVeCiPHMUAtzQWZw7RjsKjgCjhwU55QGu4U1Szw` |
|
||||
| Devnet | 3.0.0 | `SwapsVeCiPHMUAtzQWZw7RjsKjgCjhwU55QGu4U1Szw` |
|
||||
|
||||
The Token Swap Program was deployed to all networks by the Serum team at
|
||||
`SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8`, requiring a fee owner of
|
||||
`HfoTxFR1Tm6kGmWgYWD6J7YHVy1UwqSULUGVLXkJqaKN`, but that version was deprecated
|
||||
in the middle of 2021. Though that program still exists, it is not actively
|
||||
maintained.
|
||||
While third-party deployments of token-swap exist on Mainnet Beta, the team has
|
||||
no plans to deploy it themselves.
|
||||
|
||||
For devnet and testnet, please use the maintained deployment at
|
||||
`SwapsVeCiPHMUAtzQWZw7RjsKjgCjhwU55QGu4U1Szw`, and for mainnet, please use any
|
||||
other AMM project on Solana. Almost all of these were based on Token Swap!
|
||||
|
||||
Check out
|
||||
Check out the
|
||||
[program repository](https://github.com/solana-labs/solana-program-library/tree/master/token-swap)
|
||||
for more developer information.
|
||||
|
||||
|
@ -66,8 +63,7 @@ bindings](https://github.com/solana-labs/solana-program-library/blob/master/toke
|
|||
are available that support loading the Token Swap Program on to a chain and
|
||||
issuing instructions.
|
||||
|
||||
Example user interface built and maintained by Serum team is available
|
||||
[here](https://github.com/project-serum/oyster-swap)
|
||||
Example user interface is available [here](https://github.com/solana-labs/oyster-swap).
|
||||
|
||||
## Operational overview
|
||||
|
||||
|
|
|
@ -8,6 +8,11 @@ tokens from one mint to another.
|
|||
The program provides a simple mechanism for burning the original tokens and receiving
|
||||
an equal number of new tokens from an escrow account controlled by the program.
|
||||
|
||||
## Audit
|
||||
|
||||
The repository [README](https://github.com/solana-labs/solana-program-library#audits)
|
||||
contains information about program audits.
|
||||
|
||||
## Background
|
||||
|
||||
Token-2022 contains many new features for mint owners to customize the behavior
|
||||
|
|
|
@ -19,4 +19,9 @@ To build the examples and run the tests:
|
|||
|
||||
```bash
|
||||
$ make
|
||||
```
|
||||
```
|
||||
|
||||
## Audit
|
||||
|
||||
The repository [README](https://github.com/solana-labs/solana-program-library#audits)
|
||||
contains information about program audits.
|
||||
|
|
|
@ -8,3 +8,8 @@ with a live cluster.
|
|||
|
||||
The root [README](../../README.md) gives instructions on how to build and test
|
||||
these examples.
|
||||
|
||||
## Audit
|
||||
|
||||
The repository [README](https://github.com/solana-labs/solana-program-library#audits)
|
||||
contains information about program audits.
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
name = "spl-example-cross-program-invocation"
|
||||
version = "1.0.0"
|
||||
description = "Solana Program Library Cross Program Invocation Example"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Labs Maintainers <maintainers@solanalabs.com>"]
|
||||
repository = "https://github.com/solana-labs/solana-program-library"
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[features]
|
||||
|
@ -13,11 +13,11 @@ no-entrypoint = []
|
|||
test-sbf = []
|
||||
|
||||
[dependencies]
|
||||
solana-program = "1.14.6"
|
||||
solana-program = "1.14.12"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-program-test = "1.14.6"
|
||||
solana-sdk = "1.14.6"
|
||||
solana-program-test = "1.14.12"
|
||||
solana-sdk = "1.14.12"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
name = "spl-example-custom-heap"
|
||||
version = "1.0.0"
|
||||
description = "Solana Program Library Custom Heap Example"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Labs Maintainers <maintainers@solanalabs.com>"]
|
||||
repository = "https://github.com/solana-labs/solana-program-library"
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[features]
|
||||
|
@ -15,11 +15,11 @@ no-entrypoint = []
|
|||
test-sbf = []
|
||||
|
||||
[dependencies]
|
||||
solana-program = "1.14.6"
|
||||
solana-program = "1.14.12"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-program-test = "1.14.6"
|
||||
solana-sdk = "1.14.6"
|
||||
solana-program-test = "1.14.12"
|
||||
solana-sdk = "1.14.12"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
name = "spl-example-logging"
|
||||
version = "1.0.0"
|
||||
description = "Solana Program Library Logging Example"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Labs Maintainers <maintainers@solanalabs.com>"]
|
||||
repository = "https://github.com/solana-labs/solana-program-library"
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[features]
|
||||
|
@ -13,11 +13,11 @@ no-entrypoint = []
|
|||
test-sbf = []
|
||||
|
||||
[dependencies]
|
||||
solana-program = "1.14.6"
|
||||
solana-program = "1.14.12"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-program-test = "1.14.6"
|
||||
solana-sdk = "1.14.6"
|
||||
solana-program-test = "1.14.12"
|
||||
solana-sdk = "1.14.12"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
name = "spl-example-sysvar"
|
||||
version = "1.0.0"
|
||||
description = "Solana Program Library Sysvar Example"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Labs Maintainers <maintainers@solanalabs.com>"]
|
||||
repository = "https://github.com/solana-labs/solana-program-library"
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[features]
|
||||
|
@ -13,11 +13,11 @@ no-entrypoint = []
|
|||
test-sbf = []
|
||||
|
||||
[dependencies]
|
||||
solana-program = "1.14.6"
|
||||
solana-program = "1.14.12"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-program-test = "1.14.6"
|
||||
solana-sdk = "1.14.6"
|
||||
solana-program-test = "1.14.12"
|
||||
solana-sdk = "1.14.12"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
|
|
|
@ -2,21 +2,21 @@
|
|||
name = "spl-example-transfer-lamports"
|
||||
version = "1.0.0"
|
||||
description = "Solana Program Library Transfer Lamports Example"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
authors = ["Solana Labs Maintainers <maintainers@solanalabs.com>"]
|
||||
repository = "https://github.com/solana-labs/solana-program-library"
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
no-entrypoint = []
|
||||
test-sbf = []
|
||||
|
||||
[dependencies]
|
||||
solana-program = "1.14.6"
|
||||
solana-program = "1.14.12"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-program-test = "1.14.6"
|
||||
solana-sdk = "1.14.6"
|
||||
solana-program-test = "1.14.12"
|
||||
solana-sdk = "1.14.12"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
[package]
|
||||
name = "spl-example-transfer-tokens"
|
||||
version = "1.0.0"
|
||||
description = "Solana Program Library Transfer Tokens Example"
|
||||
authors = ["Solana Labs Maintainers <maintainers@solanalabs.com>"]
|
||||
repository = "https://github.com/solana-labs/solana-program-library"
|
||||
license = "Apache-2.0"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
no-entrypoint = []
|
||||
test-sbf = []
|
||||
|
||||
[dependencies]
|
||||
solana-program = "1.14.12"
|
||||
spl-token = { version = "3.5", path = "../../../token/program", features = [ "no-entrypoint" ] }
|
||||
|
||||
[dev-dependencies]
|
||||
solana-program-test = "1.14.12"
|
||||
solana-sdk = "1.14.12"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
|
@ -2,15 +2,6 @@
|
|||
|
||||
#![cfg(not(feature = "no-entrypoint"))]
|
||||
|
||||
solana_security_txt::security_txt! {
|
||||
name: "Solana Farms",
|
||||
project_url: "https://github.com/solana-labs/solana-program-library/tree/master/farms",
|
||||
contacts: "email:solana.farms@protonmail.com",
|
||||
policy: "",
|
||||
preferred_languages: "en",
|
||||
auditors: "Halborn"
|
||||
}
|
||||
|
||||
use solana_program::{
|
||||
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey,
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
//! A program demonstrating the transfer of lamports
|
||||
#![deny(missing_docs)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
mod entrypoint;
|
||||
pub mod processor;
|
|
@ -0,0 +1,75 @@
|
|||
//! Program instruction processor
|
||||
|
||||
use {
|
||||
solana_program::{
|
||||
account_info::{next_account_info, AccountInfo},
|
||||
entrypoint::ProgramResult,
|
||||
msg,
|
||||
program::invoke_signed,
|
||||
program_error::ProgramError,
|
||||
program_pack::Pack,
|
||||
pubkey::Pubkey,
|
||||
},
|
||||
spl_token::{
|
||||
instruction::transfer_checked,
|
||||
state::{Account, Mint},
|
||||
},
|
||||
};
|
||||
|
||||
/// Instruction processor
|
||||
pub fn process_instruction(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
_instruction_data: &[u8],
|
||||
) -> ProgramResult {
|
||||
// Create an iterator to safely reference accounts in the slice
|
||||
let account_info_iter = &mut accounts.iter();
|
||||
|
||||
// As part of the program specification the instruction gives:
|
||||
let source_info = next_account_info(account_info_iter)?; // 1.
|
||||
let mint_info = next_account_info(account_info_iter)?; // 2.
|
||||
let destination_info = next_account_info(account_info_iter)?; // 3.
|
||||
let authority_info = next_account_info(account_info_iter)?; // 4.
|
||||
let token_program_info = next_account_info(account_info_iter)?; // 5.
|
||||
|
||||
// In order to transfer from the source account, owned by the program-derived
|
||||
// address, we must have the correct address and seeds.
|
||||
let (expected_authority, bump_seed) = Pubkey::find_program_address(&[b"authority"], program_id);
|
||||
if expected_authority != *authority_info.key {
|
||||
return Err(ProgramError::InvalidSeeds);
|
||||
}
|
||||
|
||||
// The program transfers everything out of its account, so extract that from
|
||||
// the account data.
|
||||
let source_account = Account::unpack(&source_info.try_borrow_data()?)?;
|
||||
let amount = source_account.amount;
|
||||
|
||||
// The program uses `transfer_checked`, which requires the number of decimals
|
||||
// in the mint, so extract that from the account data too.
|
||||
let mint = Mint::unpack(&mint_info.try_borrow_data()?)?;
|
||||
let decimals = mint.decimals;
|
||||
|
||||
// Invoke the transfer
|
||||
msg!("Attempting to transfer {} tokens", amount);
|
||||
invoke_signed(
|
||||
&transfer_checked(
|
||||
token_program_info.key,
|
||||
source_info.key,
|
||||
mint_info.key,
|
||||
destination_info.key,
|
||||
authority_info.key,
|
||||
&[], // no multisig allowed
|
||||
amount,
|
||||
decimals,
|
||||
)
|
||||
.unwrap(),
|
||||
&[
|
||||
source_info.clone(),
|
||||
mint_info.clone(),
|
||||
destination_info.clone(),
|
||||
authority_info.clone(),
|
||||
token_program_info.clone(), // not required, but better for clarity
|
||||
],
|
||||
&[&[b"authority", &[bump_seed]]],
|
||||
)
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
use {
|
||||
solana_program::{
|
||||
instruction::{AccountMeta, Instruction},
|
||||
program_pack::Pack,
|
||||
pubkey::Pubkey,
|
||||
rent::Rent,
|
||||
system_instruction,
|
||||
},
|
||||
solana_program_test::{processor, tokio, ProgramTest},
|
||||
solana_sdk::{signature::Signer, signer::keypair::Keypair, transaction::Transaction},
|
||||
spl_example_transfer_tokens::processor::process_instruction,
|
||||
spl_token::state::{Account, Mint},
|
||||
std::str::FromStr,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn success() {
|
||||
// Setup some pubkeys for the accounts
|
||||
let program_id = Pubkey::from_str("TransferTokens11111111111111111111111111111").unwrap();
|
||||
let source = Keypair::new();
|
||||
let mint = Keypair::new();
|
||||
let destination = Keypair::new();
|
||||
let (authority_pubkey, _) = Pubkey::find_program_address(&[b"authority"], &program_id);
|
||||
|
||||
// Add the program to the test framework
|
||||
let program_test = ProgramTest::new(
|
||||
"spl_example_transfer_tokens",
|
||||
program_id,
|
||||
processor!(process_instruction),
|
||||
);
|
||||
let amount = 10_000;
|
||||
let decimals = 9;
|
||||
let rent = Rent::default();
|
||||
|
||||
// Start the program test
|
||||
let (mut banks_client, payer, recent_blockhash) = program_test.start().await;
|
||||
|
||||
// Setup the mint, used in `spl_token::instruction::transfer_checked`
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[
|
||||
system_instruction::create_account(
|
||||
&payer.pubkey(),
|
||||
&mint.pubkey(),
|
||||
rent.minimum_balance(Mint::LEN),
|
||||
Mint::LEN as u64,
|
||||
&spl_token::id(),
|
||||
),
|
||||
spl_token::instruction::initialize_mint(
|
||||
&spl_token::id(),
|
||||
&mint.pubkey(),
|
||||
&payer.pubkey(),
|
||||
None,
|
||||
decimals,
|
||||
)
|
||||
.unwrap(),
|
||||
],
|
||||
Some(&payer.pubkey()),
|
||||
&[&payer, &mint],
|
||||
recent_blockhash,
|
||||
);
|
||||
banks_client.process_transaction(transaction).await.unwrap();
|
||||
|
||||
// Setup the source account, owned by the program-derived address
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[
|
||||
system_instruction::create_account(
|
||||
&payer.pubkey(),
|
||||
&source.pubkey(),
|
||||
rent.minimum_balance(Account::LEN),
|
||||
Account::LEN as u64,
|
||||
&spl_token::id(),
|
||||
),
|
||||
spl_token::instruction::initialize_account(
|
||||
&spl_token::id(),
|
||||
&source.pubkey(),
|
||||
&mint.pubkey(),
|
||||
&authority_pubkey,
|
||||
)
|
||||
.unwrap(),
|
||||
],
|
||||
Some(&payer.pubkey()),
|
||||
&[&payer, &source],
|
||||
recent_blockhash,
|
||||
);
|
||||
banks_client.process_transaction(transaction).await.unwrap();
|
||||
|
||||
// Setup the destination account, used to receive tokens from the account
|
||||
// owned by the program-derived address
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[
|
||||
system_instruction::create_account(
|
||||
&payer.pubkey(),
|
||||
&destination.pubkey(),
|
||||
rent.minimum_balance(Account::LEN),
|
||||
Account::LEN as u64,
|
||||
&spl_token::id(),
|
||||
),
|
||||
spl_token::instruction::initialize_account(
|
||||
&spl_token::id(),
|
||||
&destination.pubkey(),
|
||||
&mint.pubkey(),
|
||||
&payer.pubkey(),
|
||||
)
|
||||
.unwrap(),
|
||||
],
|
||||
Some(&payer.pubkey()),
|
||||
&[&payer, &destination],
|
||||
recent_blockhash,
|
||||
);
|
||||
banks_client.process_transaction(transaction).await.unwrap();
|
||||
|
||||
// Mint some tokens to the PDA account
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[spl_token::instruction::mint_to(
|
||||
&spl_token::id(),
|
||||
&mint.pubkey(),
|
||||
&source.pubkey(),
|
||||
&payer.pubkey(),
|
||||
&[],
|
||||
amount,
|
||||
)
|
||||
.unwrap()],
|
||||
Some(&payer.pubkey()),
|
||||
&[&payer],
|
||||
recent_blockhash,
|
||||
);
|
||||
banks_client.process_transaction(transaction).await.unwrap();
|
||||
|
||||
// Create an instruction following the account order expected by the program
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[Instruction::new_with_bincode(
|
||||
program_id,
|
||||
&(),
|
||||
vec![
|
||||
AccountMeta::new(source.pubkey(), false),
|
||||
AccountMeta::new_readonly(mint.pubkey(), false),
|
||||
AccountMeta::new(destination.pubkey(), false),
|
||||
AccountMeta::new_readonly(authority_pubkey, false),
|
||||
AccountMeta::new_readonly(spl_token::id(), false),
|
||||
],
|
||||
)],
|
||||
Some(&payer.pubkey()),
|
||||
&[&payer],
|
||||
recent_blockhash,
|
||||
);
|
||||
|
||||
// See that the transaction processes successfully
|
||||
banks_client.process_transaction(transaction).await.unwrap();
|
||||
|
||||
// Check that the destination account now has `amount` tokens
|
||||
let account = banks_client
|
||||
.get_account(destination.pubkey())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let token_account = Account::unpack(&account.data).unwrap();
|
||||
assert_eq!(token_account.amount, amount);
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
/target
|
||||
Cargo.lock
|
||||
fund_stats.db
|
|
@ -1,19 +0,0 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"farm-client",
|
||||
"farm-ctrl",
|
||||
"farm-rpc",
|
||||
"farm-sdk",
|
||||
"fund",
|
||||
"router-main",
|
||||
"router-raydium",
|
||||
"router-saber",
|
||||
"router-orca",
|
||||
"vaults"
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
overflow-checks = true
|
||||
|
||||
[profile.dev]
|
||||
split-debuginfo = "unpacked"
|
|
@ -1 +0,0 @@
|
|||
docs/intro.md
|
|
@ -1,5 +0,0 @@
|
|||
This Crate is a part of Solana Farms suite which is released along with [Solana Program Library](https://github.com/solana-labs/solana-program-library/blob/master/farms).
|
||||
|
||||
Detailed information about Solana Farms project and how to use this Crate is available in the [Documentation](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/intro.md).
|
||||
|
||||
If you still have questions please contact [Support](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/support.md).
|
|
@ -1,5 +0,0 @@
|
|||
# Disclaimer
|
||||
|
||||
All claims, content, designs, algorithms, estimates, roadmaps, specifications, and performance measurements described in this project are done with the good faith efforts Solana Labs, Inc. and its affiliates ("SL"). It is up to the reader to check and validate their accuracy and truthfulness. Furthermore nothing in this project constitutes a solicitation for investment.
|
||||
Any content produced by SL or developer resources that SL provides have not been subject to audit and are for educational and inspiration purposes only. SL does not encourage, induce or sanction the deployment, integration or use of any such applications (including the code comprising the Solana blockchain protocol) in violation of applicable laws or regulations and hereby prohibits any such deployment, integration or use. This includes use of any such applications by the reader (a) in violation of export control or sanctions laws of the United States or any other applicable jurisdiction, (b) if the reader is located in or ordinarily resident in a country or territory subject to comprehensive sanctions administered by the U.S. Office of Foreign Assets Control (OFAC), or (c) if the reader is or is working on behalf of a Specially Designated National (SDN) or a person subject to similar blocking or denied party prohibitions.
|
||||
The reader should be aware that U.S. export control and sanctions laws prohibit U.S. persons (and other persons that are subject to such laws) from transacting with persons in certain countries and territories or that are on the SDN list. As a project based primarily on open-source software, it is possible that such sanctioned persons may nevertheless bypass prohibitions, obtain the code comprising the Solana blockchain protocol (or other project code or applications) and deploy, integrate, or otherwise use it. Accordingly, there is a risk to individuals that other persons using the Solana blockchain protocol may be sanctioned persons and that transactions with such persons would be a violation of U.S. export controls and sanctions law. This risk applies to individuals, organizations, and other ecosystem participants that deploy, integrate, or use the Solana blockchain protocol code directly (e.g., as a node operator), and individuals that transact on the Solana blockchain through light clients, third party interfaces, and/or wallet software.
|
|
@ -1,127 +0,0 @@
|
|||
# Farm Client CLI
|
||||
|
||||
`solana-farm-client` is a client-side command-line tool for interacting with liquidity Pools, Farms, Vaults, and Funds. It can also print on-chain metadata and perform general operations with tokens, system accounts, and governance.
|
||||
Under the hood, it leverages [Rust Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/rust_client.md), but alternatively, the same functionality can be achieved by using [HTTP Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/http_client.md) or by sending raw instructions.
|
||||
|
||||
All commands handled by `solana-farm-client` don't require Main Router admin or Fund manager privileges.
|
||||
|
||||
## General commands
|
||||
|
||||
balance Print SOL balance
|
||||
transfer Transfer SOL to another wallet
|
||||
token-address Print associated token account address
|
||||
token-balance Print token balance
|
||||
token-close Close associated token account
|
||||
token-create Create associated token account
|
||||
token-data Print token account metadata
|
||||
token-supply Print token supply
|
||||
token-transfer Transfer tokens to another wallet
|
||||
oracle-price Print oracle price
|
||||
unwrap-sol Transfer WSOL back to SOL by closing ATA
|
||||
wrap-sol Transfer SOL to the associated WSOL account
|
||||
sync-token-balance Updates token balance of the account
|
||||
wallet-balances Print all token balances for the wallet
|
||||
protocols Print description and stats of all supported protocols
|
||||
|
||||
## Metadata commands
|
||||
|
||||
get Query specified object in blockchain and print
|
||||
get-all Query all objects of the given type and print
|
||||
get-ref Query specified object by reference address and print
|
||||
list-all Query all object names of the given type and print
|
||||
|
||||
## Liquidity Pool commands
|
||||
|
||||
find-pools Find all Pools with tokens A and B
|
||||
find-pools-with-lp Find all Pools for the given LP token
|
||||
pool-price Print pool price
|
||||
swap Swap tokens in the pool
|
||||
deposit-pool Add liquidity to the pool
|
||||
withdraw-pool Remove liquidity from the pool
|
||||
|
||||
## Farm commands
|
||||
|
||||
find-farms-with-lp Find all Farms for the given LP token
|
||||
stake Stake LP tokens to the farm
|
||||
stake-balance Print user's stake balance in the farm
|
||||
unstake Unstake LP tokens from the farm
|
||||
harvest Harvest farm rewards
|
||||
|
||||
## Vault commands
|
||||
|
||||
find-vaults Find all Vaults with tokens A and B
|
||||
find-vaults-with-vt Find all Vaults for the given VT token
|
||||
crank-vault Crank single vault
|
||||
crank-vaults Crank all vaults
|
||||
deposit-vault Add liquidity to the vault
|
||||
deposit-vault-locked Add locked liquidity to the vault
|
||||
withdraw-vault Remove liquidity from the vault
|
||||
withdraw-vault-unlocked Remove unlocked liquidity from the vault
|
||||
vault-info Print vault stats
|
||||
vault-user-info Print user stats for the vault
|
||||
|
||||
## Fund commands
|
||||
|
||||
find-funds Find all Funds with Vault names matching given pattern
|
||||
request-deposit-fund Request a new deposit to the fund
|
||||
cancel-deposit-fund Cancel pending deposit to the Fund
|
||||
request-withdrawal-fund Request a new withdrawal from the Fund
|
||||
cancel-withdrawal-fund Cancel pending withdrawal from the Fund
|
||||
fund-assets Print fund assets info
|
||||
fund-custodies Print all fund custodies
|
||||
fund-custody Print fund custody info
|
||||
fund-info Print fund stats
|
||||
fund-user-info Print user stats for the fund
|
||||
fund-user-requests Print user requests for the fund
|
||||
fund-vault Print fund vault info
|
||||
fund-vaults Print all fund vaults
|
||||
start-liquidation-fund Start the Fund liquidation
|
||||
update-fund-assets-with-custodies Update fund assets info based on all custodies
|
||||
update-fund-assets-with-custody Update fund assets info based on custody holdings
|
||||
update-fund-assets-with-vault Update fund assets info based on vault holdings
|
||||
update-fund-assets-with-vaults Update fund assets info based on all vaults
|
||||
|
||||
## Governance commands
|
||||
|
||||
get-address Get governance account address
|
||||
get-config Get governance config
|
||||
get-instruction Print stored instruction in the proposal
|
||||
custody-new Create new token custody account
|
||||
proposal-new Create a new proposal
|
||||
proposal-cancel Cancel the proposal
|
||||
proposal-state Get proposal state
|
||||
sign-off Sign off the proposal
|
||||
signatory-add Add a signatory to the proposal
|
||||
signatory-remove Remove the signatory from the proposal
|
||||
tokens-deposit Deposit governing tokens
|
||||
tokens-withdraw Withdraw governing tokens
|
||||
vote-cast Cast a vote on the proposal
|
||||
vote-finalize Finalize the vote on the proposal
|
||||
vote-relinquish Remove the vote from the proposal
|
||||
instruction-execute Execute the instruction in the proposal
|
||||
instruction-flag-error Mark the instruction as failed
|
||||
instruction-insert Add a new custom instruction to the proposal
|
||||
instruction-insert-deposit-pool Add a new add liquidity to the pool instruction to the proposal
|
||||
instruction-insert-deposit-vault Add a new add liquidity to the vault instruction to the proposal
|
||||
instruction-insert-harvest Add a new harvest instruction to the proposal
|
||||
instruction-insert-program-upgrade Add a new program upgrade instruction to the proposal
|
||||
instruction-insert-stake Add a new stake instruction to the proposal
|
||||
instruction-insert-swap Add a new swap instruction to the proposal
|
||||
instruction-insert-token-transfer Add a new token transfer instruction to the proposal
|
||||
instruction-insert-unstake Add a new unstake instruction to the proposal
|
||||
instruction-insert-withdraw-fees-vault Add a new withdraw fees from the vault instruction to the proposal
|
||||
instruction-insert-withdraw-pool Add a new remove liquidity from the pool instruction to the proposal
|
||||
instruction-insert-withdraw-vault Add a new remove liquidity from the vault instruction to the proposal
|
||||
instruction-remove Remove the instruction from the proposal
|
||||
instruction-verify Verify custom instruction in the proposal
|
||||
instruction-verify-deposit-pool Verify that instruction in the proposal is an add liquidity to the pool
|
||||
instruction-verify-deposit-vault Verify that instruction in the proposal is an add liquidity to the vault
|
||||
instruction-verify-harvest Verify that instruction in the proposal is a harvest
|
||||
instruction-verify-program-upgrade Verify that instruction in the proposal is a program upgrade
|
||||
instruction-verify-stake Verify that instruction in the proposal is a stake
|
||||
instruction-verify-swap Verify that instruction in the proposal is a swap
|
||||
instruction-verify-token-transfer Verify that instruction in the proposal is a token transfer
|
||||
instruction-verify-unstake Verify that instruction in the proposal is an unstake
|
||||
instruction-verify-withdraw-fees-vault Verify that instruction in the proposal is a withdraw fees from the vault
|
||||
instruction-verify-withdraw-pool Verify that instruction in the proposal is a remove liquidity from the pool
|
||||
instruction-verify-withdraw-vault Verify that instruction in the proposal is a remove liquidity from the vault
|
|
@ -1,99 +0,0 @@
|
|||
# Farm Control CLI
|
||||
|
||||
`solana-farm-ctrl` is a command-line tool for on-chain data management, Funds, and Vaults control. It can also generate metadata for Funds, Vaults, and their liquidity tokens.
|
||||
Under the hood, it leverages [Rust Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/rust_client.md), but alternatively, the same functionality can be achieved by executing instructions directly.
|
||||
|
||||
Most of the commands executed by `solana-farm-ctrl` require Main Router admin or Fund manager privileges. It is not intended to be used by the end-users. If multisig is enabled, different admins must execute the same command multiple times until the required number of signatures is collected.
|
||||
|
||||
## Reference Database commands
|
||||
|
||||
init Initialize Reference DB on-chain
|
||||
init-all Initialize Reference DB of all storage types on-chain
|
||||
drop Drop on-chain Reference DB
|
||||
drop-all Drop on-chain Reference DB for all storage types
|
||||
print-pda-all Derive Reference DB addresses for all objects
|
||||
print-size Print Reference DB and specified object sizes
|
||||
print-size-all Print Reference DB and all object sizes
|
||||
|
||||
## Metadata commands
|
||||
|
||||
get Query specified object in blockchain and print
|
||||
get-all Query all objects of the given type and print
|
||||
get-ref Query specified object by reference address and print
|
||||
list-all Query all objects of the given type and print
|
||||
generate Generate json boilerplate for the specified object
|
||||
load Load objects from file and send to blockchain
|
||||
load-all Same as "load"
|
||||
remove Remove specified object from blockchain
|
||||
remove-all Remove all objects of the given type from blockchain
|
||||
remove-all-with-file Remove all objects in the file from blockchain
|
||||
remove-ref Remove specified reference from blockchain
|
||||
|
||||
## Vault commands
|
||||
|
||||
vault-init Initialize the Vault
|
||||
vault-crank Crank the Vault
|
||||
vault-disable-deposits Disable deposits for the specified object
|
||||
vault-disable-withdrawals Disable withdrawals for the specified object
|
||||
vault-enable-deposits Enable deposits for the specified object
|
||||
vault-enable-withdrawals Enable withdrawals for the specified object
|
||||
vault-get-info Print current stats for the Vault
|
||||
vault-get-admins Print current admin signers for the Vault
|
||||
vault-set-admins Set new admins for the Vault
|
||||
vault-set-external-fee Set new external fee percent for the Vault
|
||||
vault-set-fee Set new fee percent for the Vault
|
||||
vault-set-min-crank-interval Set new min crank interval in seconds for the Vault
|
||||
vault-shutdown Shutdown the Vault
|
||||
vault-withdraw-fees Withdraw collected fees from the Vault
|
||||
|
||||
## Fund commands
|
||||
|
||||
fund-add-custody Add a new custody to the Fund
|
||||
fund-add-vault Add a new Vault to the Fund
|
||||
fund-approve-deposit Approve pending deposit to the Fund
|
||||
fund-approve-withdrawal Approve pending withdrawal from the Fund
|
||||
fund-deny-deposit Deny pending deposit to the Fund
|
||||
fund-deny-withdrawal Deny pending withdrawal from the Fund
|
||||
fund-deposit-pool Add liquidity to the Pool in the Fund
|
||||
fund-deposit-vault Add liquidity to the Vault in the Fund
|
||||
fund-deposit-vault-locked Add locked liquidity to the Vault in the Fund
|
||||
fund-disable-deposits Disables deposits to the Fund
|
||||
fund-disable-withdrawals Disables withdrawals from the Fund
|
||||
fund-get-admins Print current admin signers for the Fund
|
||||
fund-get-info Print current stats for the Fund
|
||||
fund-harvest Harvest rewards from the Farm in the Fund
|
||||
fund-init Initialize the Fund
|
||||
fund-lock-assets Moves assets from Deposit/Withdraw custody to the Fund
|
||||
fund-remove-custody Remove the custody from the Fund
|
||||
fund-remove-vault Remove the Vault from the Fund
|
||||
fund-set-admins Set new admins for the Fund
|
||||
fund-set-assets-tracking-config Set a new assets tracking config for the Fund
|
||||
fund-set-deposit-schedule Set a new deposit schedule for the Fund
|
||||
fund-set-manager Set a new manager for the Fund
|
||||
fund-set-withdrawal-schedule Set a new withdrawal schedule for the Fund
|
||||
fund-stake Stake LP tokens to the Farm in the Fund
|
||||
fund-stop-liquidation Stop the Fund liquidation
|
||||
fund-swap Swap tokens in the Fund
|
||||
fund-unlock-assets Releases assets from the Fund to Deposit/Withdraw custody
|
||||
fund-unstake Unstake LP tokens from the Farm in the Fund
|
||||
fund-update-assets-with-custodies Update Fund assets info based on all custodies
|
||||
fund-update-assets-with-custody Update Fund assets info based on custody holdings
|
||||
fund-update-assets-with-vault Update Fund assets info based on Vault holdings
|
||||
fund-update-assets-with-vaults Update Fund assets info based on all Vaults
|
||||
fund-withdraw-fees Withdraw collected fees from the Fund
|
||||
fund-withdraw-pool Remove liquidity from the Pool in the Fund
|
||||
fund-withdraw-vault Remove liquidity from the Vault in the Fund
|
||||
fund-withdraw-vault-unlocked Remove unlocked liquidity from the Vault in the Fund
|
||||
|
||||
## Multisig commands
|
||||
|
||||
get-admins Print current admin signers for the Main Router
|
||||
set-admins Set new admins for the Main Router
|
||||
program-get-admins Print current admin signers for the program
|
||||
program-set-admins Set new admin signers for the program
|
||||
program-set-single-authority Set single upgrade authority for the program
|
||||
program-upgrade Upgrade the program from the data buffer
|
||||
|
||||
## Governance commands
|
||||
|
||||
governance init Initialize a new DAO
|
|
@ -1,56 +0,0 @@
|
|||
# Fund
|
||||
|
||||
The Fund program implements a decentralized, non-custodial, and trustless capital management protocol. It is built on top of supported liquidity protocols, including Farm Vaults. Fund Managers are responsible for selecting a portfolio of assets their Fund will hold. These assets can be in various forms: individual tokens, liquidity invested into different Pools, staked into Farms, or deposited into Farm Vaults.
|
||||
|
||||
Fund Managers are allowed to perform specific operations with tokens: swap, add/remove liquidity, stake/unstake/harvest, etc., and only in approved Pools, while all other actions are forbidden. It is enforced by the Fund program and allows investors to earn passive returns while maintaining custody of their assets (by holding Fund tokens that are minted upon each deposit and can be withdrawn for underlying assets).
|
||||
|
||||
There could be any number of Funds, each implementing its own assets management strategy. Because of the blockchain nature, historical performance and current allocations are always transparent, so investors can make an informed decision on which Funds they want to invest in.
|
||||
|
||||
## Build & Deploy
|
||||
|
||||
To run the Fund, first, build and deploy Farm programs and upload metadata as described in the [Quick Start Guide](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/quick_start.md).
|
||||
|
||||
To build and deploy the Fund program, run:
|
||||
|
||||
```sh
|
||||
cd solana-program-library/farms/fund
|
||||
cargo build-bpf
|
||||
solana program deploy ../target/deploy/solana_fund.so
|
||||
```
|
||||
|
||||
Integration tests are located in the `fund/tests` directory and can be started as follows:
|
||||
|
||||
```sh
|
||||
cargo test -- --nocapture --test-threads=1 --ignored
|
||||
```
|
||||
|
||||
These tests execute transactions on mainnet, which will cost you some SOL.
|
||||
|
||||
The next step is to generate Fund metadata:
|
||||
|
||||
```sh
|
||||
solana-farm-ctrl --keypair main_admin.json generate Fund [FUND_PROGRAM_ADDRESS] [FUND_NAME]
|
||||
```
|
||||
|
||||
Generated metadata should be manually saved to JSON files (tokens and funds separately) using a format similar to other tokens and vault files. And then uploaded with:
|
||||
|
||||
```sh
|
||||
solana-farm-ctrl --keypair main_admin.json load token fund_tokens.json
|
||||
solana-farm-ctrl --keypair main_admin.json load fund funds.json
|
||||
```
|
||||
|
||||
To verify metadata and installation, run `solana-farm-ctrl get-all fund` and `solana-farm-client fund-info [FUND_NAME]`.
|
||||
|
||||
## Initialization
|
||||
|
||||
Before a Fund can be used it, must be initialized with `solana-farm-ctrl fund-init [FUNDNAME]`. Instead of executing commands with `Farm Ctrl CLI`, alternatively, you can create and send corresponding instructions with [HTTP Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/http_client.md) or [Rust Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/rust_client.md).
|
||||
|
||||
Each Fund has three types of configuration settings. First is Fund Assets Tracking Config, which can be set with `fund-set-assets-tracking-config`. It defines parameters such as assets limit, oracle price minimum quality, and whether or not to issue Fund tokens to depositors. Only admins are allowed to alter these settings. The other configs are Deposit and Withdrawal Schedules, which can be set with `fund-set-deposit-schedule` and `fund-set-withdrawal-schedule`. They define when deposits/withdrawals can be made, whether they will require the approval of the Fund Manager, fees, and amount limits. Deposit/Withdrawal configs can be set or modified by admins or Fund managers.
|
||||
|
||||
Funds keep all assets in custodies. There are two types of custodies - DepositWithdraw and Trading. The former is used to accept initial deposits or process withdrawals and the latter to perform trading operations. Custodies can be added with `fund-add-custody`. Users will be allowed to make deposits and withdrawals only in tokens for which DepositWithdraw custodies have been created. Similarly, trading operations (like swaps, adding liquidity to a Farm or Vault, etc.) require corresponding Trading custodies to be pre-created, including the ones for holding LP tokens. Fund Manager can move tokens from DepositWithdraw custody to Trading or vice versa using `fund-lock-assets` and `fund-unlock-assets`.
|
||||
|
||||
Liquidity Pools, Farms, and Vaults that particular Fund will be allowed to trade in or deposit liquidity to must be explicitly whitelisted. This can be done with `fund-add-vault` and require admin privileges.
|
||||
|
||||
Fund Managers perform trading operations, deposit and withdrawal approvals (if enabled). New Fund Manager can be set with `fund-set-manager`.
|
||||
|
||||
Whether the Fund is properly initialized can be verified with client tools, e.g. `solana-farm-client fund-info [FUND_NAME]`.
|
|
@ -1,36 +0,0 @@
|
|||
# Governance
|
||||
|
||||
To initialize the DAO, first build and deploy the governance program:
|
||||
|
||||
```sh
|
||||
cd solana-program-library/governance/program
|
||||
cargo build-bpf
|
||||
solana program deploy --commitment finalized target/deploy/spl_governance.so
|
||||
```
|
||||
|
||||
Then initialize the DAO using the Main Router admin account with:
|
||||
|
||||
```sh
|
||||
solana-farm-ctrl --keypair main_admin.json governance init [DAO_PROGRAM_ADDRESS] [DAO_TOKENS_TO_MINT]
|
||||
```
|
||||
|
||||
It will take over on-chain programs upgrade authorities (including the DAO program itself) and DAO mint. Realm authority will also be removed. DAO tokens will be deposited to the admin account for further distribution.
|
||||
|
||||
Farm client can be used to perform all DAO operations: create proposals, deposit tokens, sign-off, add or execute instructions, vote, etc. See help for details:
|
||||
|
||||
```sh
|
||||
solana-farm-client governance help
|
||||
```
|
||||
|
||||
As part of DAO initialization, SOL token custody will be created (and more tokens can be added permissionless). Custody can be used to govern all interactions with Pools, Farms, or Vaults. It is useful if a third party manages funds, and every operation must be voted on first. [Rust Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/rust_client.md) or [Farm Client CLI](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/farm_client_cli.md) simplify instruction creation and verification process, here is a workflow example for already initialized DAO:
|
||||
|
||||
```sh
|
||||
solana-farm-client governance proposal-new FarmCustodyGovernance SwapTokens http://description.com 0
|
||||
solana-farm-client governance signatory-add FarmCustodyGovernance 0 J7paVZ8axBfUaGFDNknc7XF3GHjVLZzvL57FaCuxjJo7
|
||||
solana-farm-client governance instruction-insert-swap FarmCustodyGovernance 0 0 RDM RAY SRM 1.0 0.0
|
||||
solana-farm-client -k signer.json governance sign-off FarmCustodyGovernance 0
|
||||
solana-farm-client -k voter.json governance instruction-verify-swap FarmCustodyGovernance 0 0 RDM RAY SRM 1.0 0.0
|
||||
solana-farm-client -k voter.json governance vote-cast FarmCustodyGovernance 0 1
|
||||
solana-farm-client governance vote-finalize FarmCustodyGovernance 0
|
||||
solana-farm-client -k anyone.json governance instruction-execute FarmCustodyGovernance 0 0
|
||||
```
|
File diff suppressed because it is too large
Load Diff
|
@ -1,57 +0,0 @@
|
|||
# Solana Farms
|
||||
|
||||
## Introduction
|
||||
|
||||
Solana Farms is a collection of easy-to-use tools and blockchain contracts for yield optimization strategies.
|
||||
|
||||
It is powered by the Solana blockchain to allow for frequent automatic compounding, staking, and rebalancing.
|
||||
|
||||
One of the distinct features of this platform is the on-chain Reference Database. Metadata for all objects: Tokens, Pools, Farms, Vaults, Funds, etc., is stored in the blockchain, so clients don't need any state or hard-coded data.
|
||||
|
||||
Solana Farms provides a unified interface to Funds, Vaults, regular AMM Pools, Farms, and basic operations on tokens and accounts. Raydium, Saber, and Orca protocols are currently supported, but others are under development.
|
||||
|
||||
This source code is an example that third parties can utilize to create and use their own version of a yield farming or aggregation service.
|
||||
|
||||
## Quick start
|
||||
|
||||
To quickly build, test and deploy Solana Farms and try it out in action, please follow the [Quick Start](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/quick_start.md) guide.
|
||||
|
||||
## Dive in
|
||||
|
||||
If you want to learn more about the tools and building blocks of the Solana Farms suite, follow the links below:
|
||||
|
||||
[Rust Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/rust_client.md)
|
||||
|
||||
[Http Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/http_client.md)
|
||||
|
||||
[Farm Client CLI](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/farm_client_cli.md)
|
||||
|
||||
[Farm Control CLI](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/farm_ctrl_cli.md)
|
||||
|
||||
[Farm SDK](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/sdk.md)
|
||||
|
||||
[Routers](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/routers.md)
|
||||
|
||||
[Vaults](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/vaults.md)
|
||||
|
||||
[Fund](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/fund.md)
|
||||
|
||||
[Multisig](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/multisig.md)
|
||||
|
||||
[Governance](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/governance.md)
|
||||
|
||||
## Support
|
||||
|
||||
For details on how to get help or report a problem, see the [Support](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/support.md) page.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are very welcome. Please refer to the [Contributing](https://github.com/solana-labs/solana/blob/master/CONTRIBUTING.md) guidelines for more information.
|
||||
|
||||
## License
|
||||
|
||||
Solana Farms is a part of the Solana Program Library, which is released under this [License](https://github.com/solana-labs/solana-program-library/blob/master/LICENSE).
|
||||
|
||||
## Disclaimer
|
||||
|
||||
By accessing or using Solana Farms or any of its components, you accept and agree with the [Disclaimer](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/disclaimer.md).
|
|
@ -1,26 +0,0 @@
|
|||
# Multisig
|
||||
|
||||
Multi-signature mode is supported for all Main Router, Vault, and Fund transactions that require admin privileges, including program upgrades. To enable multisig for Main Router use:
|
||||
|
||||
```sh
|
||||
solana-farm-ctrl set-admins [MIN_SIGNATURES] [ADMIN_KEY1] [ADMIN_KEY2]...
|
||||
```
|
||||
|
||||
The maximum number of admin signers is six.
|
||||
|
||||
To enable multisig for program upgrades, Vaults, and Funds use `solana-farm-ctrl program-set-admins`, `solana-farm-ctrl vault-set-admins`, and `solana-farm-ctrl fund-set-admins`. Alternatively, you can use [Rust Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/rust_client.md) or build and send raw instructions.
|
||||
|
||||
Multisig is fully transparent, no changes are required to how clients build or submit transactions. If a transaction requires multisig, API calls will return Ok (meaning intent was recorded on-chain) but won't be executed until enough signatures have been accumulated.
|
||||
|
||||
If program upgrades multisig is enabled, then upgrades must be performed via Main Router using `solana-farm-ctrl program-upgrade`, e.g.:
|
||||
|
||||
```sh
|
||||
# write program to an on-chain buffer and get the buffer address
|
||||
solana program write-buffer solana_router_raydium.so
|
||||
solana program set-buffer-authority --new-buffer-authority [MULTISIG_ADDRESS] [BUFFER_ADDRESS]
|
||||
solana-farm-ctrl program-upgrade [PROGRAM_ID] [BUFFER_ADDRESS]
|
||||
```
|
||||
|
||||
Commands that initialize multisig can be used again to amend the existing set of admins (it will have to be signed by admins like other multisig transactions). To reset program upgrade multisig back to a single authority, use `solana-farm-ctrl program-set-single-authority`.
|
||||
|
||||
To get the current list of admins you can use `get-admins`, `program-get-admins`, `vault-get-admins` and `fund-get-admins`.
|
|
@ -1,207 +0,0 @@
|
|||
# Quick Start
|
||||
|
||||
## Setup Environment
|
||||
|
||||
1. Clone the repository from https://github.com/solana-labs/solana-program-library.git
|
||||
2. Install the latest Solana tools from https://docs.solana.com/cli/install-solana-cli-tools. If you already have Solana tools, run `solana-install update` to get the latest compatible version.
|
||||
3. Install the latest Rust stable from https://rustup.rs/. If you already have Rust, run `rustup update` to get the latest version.
|
||||
4. Install the `libudev` development package for your distribution (`libudev-dev` on Debian-derived distros, `libudev-devel` on Redhat-derived).
|
||||
5. Install the `libsqlite3` development package for your distribution (`libsqlite3-dev` on Debian-derived distros, `sqlite-devel` on Redhat-derived).
|
||||
6. If you get an error about missing `libssl.so.1.1` file while building on-chain programs, install libssl1.1 package. It is missing in the standard repositories for the most recent versions of Ubuntu, so you might need to install it manually like this:
|
||||
|
||||
```
|
||||
wget http://nz2.archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1l-1ubuntu1.5_amd64.deb
|
||||
sudo dpkg -i ./libssl1.1_1.1.1l-1ubuntu1.5_amd64.deb
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
Before starting the build, set `MAIN_ROUTER_ID` and `MAIN_ROUTER_ADMIN` environment variables. They should point to the existing Main Router program and admin account or generate a new set of keys if you plan to maintain your own version of the reference database:
|
||||
|
||||
```
|
||||
solana-keygen new -o main_admin.json
|
||||
solana-keygen new -o main_router.json
|
||||
```
|
||||
|
||||
To build all of the off-chain libraries and programs, run the `cargo build` command from the `farms` directory:
|
||||
|
||||
```sh
|
||||
export MAIN_ROUTER_ID="replace this with main router id (solana main_router.json address)"
|
||||
export MAIN_ROUTER_ADMIN="replace this with admin pubkey (solana main_admin.json address)"
|
||||
cd solana-program-library/farms
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
Binaries will be placed under `solana-program-library/farms/target/release` directory. If you plan on using `solana-farm-ctrl` or `solana-farm-client` tools per the examples below, you might want to add the release path to your environment's `PATH` variable:
|
||||
|
||||
```sh
|
||||
pushd target/release
|
||||
export PATH=$PATH:$(pwd)
|
||||
popd
|
||||
```
|
||||
|
||||
Alternatively, you can execute instructions directly using [HTTP Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/http_client.md) or [Rust Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/rust_client.md).
|
||||
|
||||
To build on-chain programs, use the standard `cargo build-bpf` command for Solana programs:
|
||||
|
||||
```sh
|
||||
for program in router-*; do
|
||||
pushd $program &>/dev/null
|
||||
cargo build-bpf
|
||||
popd &>/dev/null
|
||||
done
|
||||
```
|
||||
|
||||
To build Vaults, specify an additional argument that tells the compiler which strategy needs to be built:
|
||||
|
||||
```sh
|
||||
pushd vaults
|
||||
cargo build-bpf --no-default-features --features SBR-STAKE-LP-COMPOUND
|
||||
popd
|
||||
```
|
||||
|
||||
Every time you re-build the vault program with another strategy, it overwrites the same file (target/deploy/solana_vaults.so), so you should build and deploy multiple strategies one by one and specify which address to deploy to, e.g.:
|
||||
|
||||
```sh
|
||||
pushd vaults &>/dev/null
|
||||
for strategy in RDM SBR ORC; do
|
||||
solana-keygen new -o vault_$strategy.json
|
||||
cargo build-bpf --no-default-features --features $strategy-STAKE-LP-COMPOUND
|
||||
solana program deploy --program-id vault_$strategy.json target/deploy/solana_vaults.so
|
||||
done
|
||||
popd &>/dev/null
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
Tests are executed with the `cargo test` command:
|
||||
|
||||
```sh
|
||||
cargo test
|
||||
```
|
||||
|
||||
Integration tests are located in `farm-client/tests` directory and can be started as follows:
|
||||
|
||||
```sh
|
||||
cargo test -- --nocapture --test-threads=1 --ignored
|
||||
```
|
||||
|
||||
Remember that integration tests execute transactions on mainnet, which will cost you some SOL, and require on-chain programs and reference database to be deployed beforehand.
|
||||
|
||||
## Deploy
|
||||
|
||||
To deploy on-chain programs, use the standard `solana program deploy`:
|
||||
|
||||
```sh
|
||||
solana program deploy target/deploy/solana_router_raydium.so
|
||||
solana program deploy target/deploy/solana_router_saber.so
|
||||
solana program deploy target/deploy/solana_router_orca.so
|
||||
```
|
||||
|
||||
If you generated your own set of keys for Main Router ID and Main Router admin, you need to deploy the Main Router program while specifying the corresponding upgrade authority and program id:
|
||||
|
||||
```sh
|
||||
solana program deploy --upgrade-authority main_admin.json --program-id main_router.json target/deploy/solana_router_main.so
|
||||
```
|
||||
|
||||
## Upload Metadata
|
||||
|
||||
This project uses an on-chain reference database to store the required metadata. If you plan to maintain your own copy of the database (i.e., generated a new pair of Main Router keys and deployed Main Router), you need to initialize the storage and upload metadata. Otherwise, skip this step.
|
||||
|
||||
First, generate PDA addresses for the RefDB indexes:
|
||||
|
||||
```sh
|
||||
solana-farm-ctrl print-pda-all
|
||||
```
|
||||
|
||||
Update `farm-ctrl/metadata/programs/programs.json` with newly generated addresses and addresses of your deployed programs (anything that has a blank address in that file needs to be updated or deleted if not needed).
|
||||
|
||||
Initialize the storage (Note: it will cost you about 5 SOL):
|
||||
|
||||
```sh
|
||||
solana-farm-ctrl --keypair main_admin.json init-all
|
||||
```
|
||||
|
||||
Metadata for external protocols, like Raydium, needs to be extracted from relative sources. For convenience, scripts for data download and data itself are available in the `farms/farm-ctrl/metadata` directory.
|
||||
To upload metadata, run:
|
||||
|
||||
```sh
|
||||
solana-farm-ctrl -k main_admin.json load --skip-existing Token farm-ctrl/metadata/tokens/solana_token_list/filtered_tokens.json
|
||||
solana-farm-ctrl -k main_admin.json load --skip-existing Token farm-ctrl/metadata/pools/raydium/pools.json
|
||||
solana-farm-ctrl -k main_admin.json load --skip-existing Token farm-ctrl/metadata/pools/saber/pools.json
|
||||
solana-farm-ctrl -k main_admin.json load --skip-existing Token farm-ctrl/metadata/pools/orca/pools.json
|
||||
solana-farm-ctrl -k main_admin.json load --skip-existing Token farm-ctrl/metadata/farms/orca/farms.json
|
||||
solana-farm-ctrl -k main_admin.json load --skip-existing Pool farm-ctrl/metadata/pools/raydium/pools.json
|
||||
solana-farm-ctrl -k main_admin.json load --skip-existing Farm farm-ctrl/metadata/farms/raydium/farms.json
|
||||
solana-farm-ctrl -k main_admin.json load --skip-existing Pool farm-ctrl/metadata/pools/saber/pools_and_farms.json
|
||||
solana-farm-ctrl -k main_admin.json load --skip-existing Farm farm-ctrl/metadata/farms/saber/pools_and_farms.json
|
||||
solana-farm-ctrl -k main_admin.json load --skip-existing Pool farm-ctrl/metadata/pools/orca/pools.json
|
||||
solana-farm-ctrl -k main_admin.json load --skip-existing Farm farm-ctrl/metadata/farms/orca/farms.json
|
||||
```
|
||||
|
||||
Metadata has interdependencies, so it needs to be uploaded sequentially as per the list above, don't run it in parallel even for the data of the same type. Metadata upload will cost you some SOL, depending on the number of records. You can get the price per record (Target) and max number of records (Target Max) by running this command:
|
||||
|
||||
```sh
|
||||
solana-farm-ctrl print-size-all
|
||||
```
|
||||
|
||||
To generate metadata for all Raydium Vaults, run:
|
||||
|
||||
```sh
|
||||
./farm-ctrl/metadata/vaults/generate_vaults.py vaults_rdm.json tokens_rdm.json [VAULT_PROG_ID] RDM
|
||||
```
|
||||
|
||||
And then upload it:
|
||||
|
||||
```sh
|
||||
solana-farm-ctrl --keypair main_admin.json load token tokens_rdm.json
|
||||
solana-farm-ctrl --keypair main_admin.json load vault vaults_rdm.json
|
||||
```
|
||||
|
||||
Similarly, metadata can be generated and uploaded for Orca and Saber Vaults. Also, it is possible to generate metadata only for a single Vault with:
|
||||
|
||||
```sh
|
||||
solana-farm-ctrl --keypair main_admin.json generate Vault [VAULT_PROGRAM_ADDRESS] [VAULT_NAME] [VAULT_TOKEN_NAME]
|
||||
```
|
||||
|
||||
To verify metadata you can run `solana-farm-ctrl list-all vault` or `solana-farm-ctrl get-all vault`.
|
||||
|
||||
## Run
|
||||
|
||||
After metadata for Vaults and Vault tokens have been uploaded, Vaults need to be initialized with:
|
||||
|
||||
```sh
|
||||
solana-farm-ctrl vault-init all
|
||||
solana-farm-ctrl vault-enable-deposits all
|
||||
solana-farm-ctrl vault-enable-withdrawals all
|
||||
```
|
||||
|
||||
And then, you can try one of the client commands to verify the installation:
|
||||
|
||||
```sh
|
||||
solana-farm-client deposit-vault [VAULT_NAME] [TOKEN_A_AMOUNT] 0
|
||||
solana-farm-client crank-vault [VAULT_NAME] 1
|
||||
solana-farm-client vault-info [VAULT_NAME]
|
||||
solana-farm-client swap RDM SOL USDC 0.1
|
||||
```
|
||||
|
||||
For more information see [Vaults](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/vaults.md), for more usage examples see [Rust Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/rust_client.md) or [HTTP Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/http_client.md).
|
||||
|
||||
To use HTTP RPC service, start it with:
|
||||
|
||||
```sh
|
||||
solana-farm-rpc --farm-client-url https://solana-api.projectserum.com --http-rpc-url http://0.0.0.0:9090
|
||||
```
|
||||
|
||||
Note that RPC service should be adequately scaled and put behind a load balancer and HTTPS proxy for production use.
|
||||
|
||||
Open http://127.0.0.1:9090 in a browser to see available methods. You can also use [SwaggerHub](https://app.swaggerhub.com/apis-docs/ska22/SolanaFarms/0.1) to call any method interactively. Swagger schema is available in `solana-program-library/farms/farm-rpc/swagger.yaml`.
|
||||
|
||||
## Further Steps
|
||||
|
||||
Now, when everything is up and running, you may also consider:
|
||||
|
||||
- Write [HTTP Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/http_client.md) or [Rust Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/rust_client.md)
|
||||
- Initialize a decentralized [Fund](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/fund.md)
|
||||
- Enable [Multisig](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/multisig.md) for admin operations
|
||||
- Enable [Governance / DAO](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/governance.md)
|
|
@ -1,11 +0,0 @@
|
|||
# Main Router
|
||||
|
||||
Main Router is an on-chain program that handles the creation, updates, and deletion of all metadata objects: tokens, pools, farms, vaults, program IDs, and generic key-value records, such as user or vault stats.
|
||||
|
||||
Interaction with the Main Router can be done via [Farm Control CLI](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/farm_ctrl_cli.md), [Rust Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/rust_client.md), or calling instructions directly. The list of instructions can be found [here](https://github.com/solana-labs/solana-program-library/blob/master/farms/farm-sdk/src/instruction/main_router.rs).
|
||||
|
||||
# Protocol Routers
|
||||
|
||||
Protocol Routers are on-chain programs with a common interface for interaction with Raydium, Saber, and Orca Pools and Farms. They perform in and out amounts calculations and safety checks for tokens spent and received. They don't hold user funds but validate, wrap, and send instructions to the AMMs and Farms.
|
||||
|
||||
Interaction with Protocol Routers can be done via [Farm Client CLI](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/farm_client_cli.md), [Rust Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/rust_client.md), [HTTP Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/http_client.md), or calling instructions directly. The list of instructions can be found [here](https://github.com/solana-labs/solana-program-library/blob/master/farms/farm-sdk/src/instruction/amm.rs).
|
|
@ -1,188 +0,0 @@
|
|||
# Rust Client
|
||||
|
||||
Rust Client is a library that provides an easy way for off-chain Rust programs to interact with Routers, Pools, Funds, Vaults, and Funds, perform admin operations, metadata queries, and some common operations with wallets and accounts.
|
||||
|
||||
Rust Client is a part of Farms suite, and to use it, existing deployment of Farm programs and metadata must be present per [Quick Start Guide](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/quick_start.md).
|
||||
|
||||
The library needs to be specified as a dependency in your Cargo.toml:
|
||||
|
||||
```
|
||||
[dependencies]
|
||||
solana-farm-client = "1.1.2"
|
||||
```
|
||||
|
||||
And declared in a program:
|
||||
|
||||
```rust
|
||||
use solana_farm_client::client::FarmClient;
|
||||
|
||||
let client = FarmClient::new("https://api.mainnet-beta.solana.com");
|
||||
let keypair = FarmClient::read_keypair_from_file(
|
||||
&(std::env::var("HOME").unwrap().to_string() + "/.config/solana/id.json"),
|
||||
);
|
||||
```
|
||||
|
||||
Client's methods accept human-readable names (tokens, polls, etc.) and UI (decimal) amounts, so you can simply call:
|
||||
|
||||
```rust
|
||||
client.swap(&keypair, "RDM", "SOL", "RAY", 0.1);
|
||||
client.add_liquidity(&keypair, "RDM.RAY-SOL", 0.0, 0.1);
|
||||
client.stake(&keypair, "RDM.RAY-SOL", 0.0);
|
||||
client.harvest(&keypair, "RDM.RAY-SOL");
|
||||
```
|
||||
|
||||
to swap 0.1 SOL for RAY, deposit RAY and SOL to a Raydium pool, stake LP tokens, and harvest rewards. `RDM` above is a route or protocol. Use `SBR` for Saber pools and `ORC` for Orca. All metadata required to lookup account addresses, decimals, etc., is stored on-chain.
|
||||
|
||||
Rust Client caches metadata to make subsequent calls faster, most noticeably queries that list a large number of objects, like `get_tokens()`. It also leverages RefDB to detect if any metadata objects have changed (e.g., any of the tokens) and reloads them if that is the case.
|
||||
|
||||
Under the hood Client uses the official Solana RPC Client which can be accessed with
|
||||
client.rpc_client, for example: `client.rpc_client.get_latest_blockhash()`.
|
||||
|
||||
The naming convention for Pools and Farms is `[PROTOCOL].[TOKEN_A]-[TOKEN_B]-[VERSION]`.
|
||||
Naming convention for Vaults is `[PROTOCOL].[STRATEGY].[TOKEN_A]-[TOKEN_B]-[VERSION]`.
|
||||
There are single token pools where `[TOKEN_B]` is not present.
|
||||
A list of supported protocols can be obtained with `get_protocols()`.
|
||||
If `[VERSION]` is omitted, then Pool, Farm, or Vault with the latest version will be used.
|
||||
|
||||
A few examples:
|
||||
|
||||
```rust
|
||||
// get SOL account balance
|
||||
client.get_account_balance(&keypair.pubkey());
|
||||
|
||||
// get SPL token account balance
|
||||
client.get_token_account_balance(&keypair.pubkey(), "SRM");
|
||||
|
||||
// get token metadata
|
||||
client.get_token("SRM");
|
||||
|
||||
// find Raydium pools with RAY and SRM tokens
|
||||
client.find_pools(Protocol::Raydium, "RAY", "SRM");
|
||||
|
||||
// find Saber pools with USDC and USDT tokens
|
||||
client.find_pools(Protocol::Saber, "USDC", "USDT");
|
||||
|
||||
// get pool metadata
|
||||
client.get_pool("RDM.RAY-SRM");
|
||||
|
||||
// get farm metadata
|
||||
client.get_farm("RDM.RAY-SRM");
|
||||
|
||||
// find all vaults with RAY and SRM tokens
|
||||
client.find_vaults("RAY", "SRM");
|
||||
|
||||
// get vault metadata
|
||||
client.get_vault("RDM.STC.RAY-SRM");
|
||||
|
||||
// get fund metadata
|
||||
client.get_fund("TestFund");
|
||||
|
||||
// get the list of all pools
|
||||
client.get_pools();
|
||||
|
||||
// find farms for specific LP token
|
||||
client.find_farms_with_lp("LP.RDM.RAY-SRM-V4");
|
||||
|
||||
// get Raydium pool price
|
||||
client.get_pool_price("RDM.RAY-SRM");
|
||||
// or specify version for specific pool
|
||||
client.get_pool_price("RDM.RAY-SRM-V4");
|
||||
|
||||
// get oracle price
|
||||
client.get_oracle_price("SOL", 0, 0.0);
|
||||
|
||||
// list official program IDs
|
||||
client.get_program_ids();
|
||||
|
||||
// swap in the Raydium pool
|
||||
client.swap(&keypair, Protocol::Raydium, "SOL", "RAY", 0.01, 0.0);
|
||||
|
||||
// swap in the Saber pool
|
||||
client.swap(&keypair, Protocol::Saber, "USDC", "USDT", 0.01, 0.0);
|
||||
|
||||
// deposit liquidity to the Raydium pool (zero second token amount means calculate it automatically)
|
||||
client.add_liquidity_pool(&keypair, "RDM.GRAPE-USDC", 0.1, 0.0);
|
||||
|
||||
// withdraw your liquidity from the Raydium pool (zero amount means remove all tokens)
|
||||
client.remove_liquidity_pool(&keypair, "RDM.GRAPE-USDC", 0.0);
|
||||
|
||||
// stake LP tokens to the Raydium farm (zero amount means stake all)
|
||||
client.stake(&keypair, "RDM.GRAPE-USDC", 0.0);
|
||||
|
||||
// get staked balance
|
||||
client.get_user_stake_balance(&keypair.pubkey(), "RDM.GRAPE-USDC");
|
||||
|
||||
// harvest rewards
|
||||
client.harvest(&keypair, "RDM.GRAPE-USDC");
|
||||
|
||||
// unstake LP tokens from the farm (zero amount means unstake all)
|
||||
client.unstake(&keypair, "RDM.GRAPE-USDC", 0.0);
|
||||
|
||||
// deposit liquidity to the vault (zero second token amount means calculate it automatically)
|
||||
client.add_liquidity_vault(&keypair, "RDM.STC.RAY-SRM", 0.01, 0.0);
|
||||
|
||||
// withdraw liquidity from the vault (zero amount means remove all tokens)
|
||||
client.remove_liquidity_vault(&keypair, "RDM.STC.RAY-SRM", 0.0);
|
||||
|
||||
// request liquidity deposit to the fund
|
||||
client.request_deposit_fund(&keypair, "TestFund", "USDC", 0.01);
|
||||
|
||||
// request liquidity withdrawal from the fund (zero amount means withdraw everything)
|
||||
client.request_withdrawal_fund(&keypair, "TestFund", "USDC", 0.0);
|
||||
|
||||
// list all vaults that belong to particular fund
|
||||
client.get_fund_vaults("TestFund");
|
||||
|
||||
// transfer SOL to another wallet
|
||||
client.transfer(&keypair, &Pubkey::new_unique(), 0.001);
|
||||
|
||||
// transfer SPL tokens to another wallet
|
||||
client.token_transfer(&keypair, "SRM", &Pubkey::new_unique(), 0.001);
|
||||
|
||||
// create associated token account for the wallet
|
||||
client.get_or_create_token_account(&keypair, "SRM");
|
||||
|
||||
// list all active token accounts for the wallet
|
||||
client.get_wallet_tokens(&keypair.pubkey());
|
||||
|
||||
// get vault stats
|
||||
client.get_vault_info("RDM.STC.RAY-SRM");
|
||||
|
||||
// get user stats for particular vault
|
||||
client.get_vault_user_info(&keypair.pubkey(), "RDM.STC.RAY-SRM");
|
||||
|
||||
// get fund stats and parameters
|
||||
client.get_fund_info("TestFund");
|
||||
|
||||
// get fund custody info
|
||||
client.get_fund_custody("TestFund", "USDC", FundCustodyType::DepositWithdraw);
|
||||
|
||||
// get information about fund assets
|
||||
client.get_fund_assets(&fund_name, FundAssetType::Vault);
|
||||
client.get_fund_assets(&fund_name, FundAssetType::Custody);
|
||||
```
|
||||
|
||||
The Client also allows for building raw unsigned instructions that can be integrated into more complex workflows:
|
||||
|
||||
```rust
|
||||
// create a new instruction for cranking a Vault, neither sign nor send it
|
||||
let inst = client.new_instruction_crank_vault(&keypair.pubkey(), "RDM.STC.RAY-SRM");
|
||||
```
|
||||
|
||||
You can then sign and send it with:
|
||||
|
||||
```rust
|
||||
client.sign_and_send_instructions(&[keypair], &[inst]);
|
||||
```
|
||||
|
||||
Some actions may require multiple instructions to be executed. To handle such cases, there are methods with names starting with `all_instructions_`, and they return a vector of instructions. For example:
|
||||
|
||||
```rust
|
||||
// create a single instruction for depositing liquidity to the vault assuming all prerequisites are met
|
||||
client.new_instruction_add_liquidity_vault(&keypair.pubkey(), "RDM.STC.RAY-SRM", 0.1, 0.0);
|
||||
|
||||
// create potentially multiple instructions that would handle new token accounts creation, user initialization, token wrap/unwrap, etc.
|
||||
client.all_instructions_add_liquidity_vault(&keypair.pubkey(), "RDM.STC.RAY-SRM", 0.1, 0.0);
|
||||
```
|
||||
|
||||
In addition to the library, there is also a command-line tool that sets an example for basic usage of the library: [Farm Client CLI](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/farm_client_cli.md).
|
|
@ -1,12 +0,0 @@
|
|||
# Farm SDK
|
||||
|
||||
Farm SDK is a lower-level Rust library with a common code that is used by all Solana Farms tools and contracts. You might only need this SDK if you plan to build your own on-chain program or custom client that uses some of the SDK's functionality. Otherwise you should be using existing [HTTP Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/http_client.md) or [Rust Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/rust_client.md).
|
||||
|
||||
To use the library, specify it in the `[dependencies]` section of your Cargo.toml, e.g.:
|
||||
|
||||
```
|
||||
[dependencies]
|
||||
solana-farm-sdk = "1.1.2"
|
||||
```
|
||||
|
||||
The best way to learn what can be done with SDK is to look at the source code, which can be found [here](https://github.com/solana-labs/solana-program-library/tree/master/farms/farm-sdk/src).
|
|
@ -1,7 +0,0 @@
|
|||
# Support
|
||||
|
||||
If you are experiencing technical difficulties while writing code or interacting with the Solana blockchain, ask your questions on [Discord](https://discord.gg/RxeGBH), [Telegram](https://t.me/solana), or [StackOverflow](https://stackoverflow.com/questions/tagged/solana) (tag your question with `solana`).
|
||||
|
||||
If you have Solana Farms specific questions or issues integrating Farms code, feel free to ask in the `developer-support` channel on [Discord](https://discord.gg/RxeGBH), but please mention that this is `solana farms` related. Or reach out to solana.farms@protonmail.com and provide your name, a link to the project website (if there is one), and a detailed description of the problem.
|
||||
|
||||
If you found a bug in the code, you can raise an issue on [Github](https://github.com/solana-labs/solana-program-library). But if this is a security issue, please don't open it on Github or disclose it in public channels. Send information to the email above instead.
|
|
@ -1,31 +0,0 @@
|
|||
# Vaults
|
||||
|
||||
Vaults are on-chain programs that implement various yield farming strategies. Under the hood, they interact with underlying Liquidity Pools and other Defi protocols to generate an extra yield for users that wouldn't be possible with passive investments. Individual yield farming strategies are stored under the `strategies` sub-folder.
|
||||
|
||||
`RDM-STAKE-LP-COMPOUND` strategy works as follows:
|
||||
|
||||
- User deposits tokens into a Vault with add_liquidity transaction. For example, Vault `RDM.STC.RAY-SRM` takes RAY and SRM tokens. To get a list of available Vaults, one can use the `client.get_vaults()` function or `api/v1/vaults` RPC call. Vault tokens are minted back to the user to represent their share in the Vault.
|
||||
- Vault sends user tokens to the corresponding Raydium Pool, receives LP tokens, and stakes them to the Raydium Farm.
|
||||
- Vaults should be cranked on a periodic basis. Crank operation is permissionless and can be done by anyone. And it is executed for the entire Vault, not per individual user. Crank consists of three steps:
|
||||
1. Harvest Farm rewards (in one or both tokens);
|
||||
2. Rebalance rewards to get proper amounts of each token;
|
||||
3. Place rewards back into the Pool and stake received LP tokens. A small Vault fee is taken from rewards, and it can be used to incentivize Crank operations.
|
||||
- Upon liquidity removal, the user gets original tokens back in amounts proportional to Vault tokens they hold. Vault tokens are then burned.
|
||||
|
||||
`SBR-STAKE-LP-COMPOUND` and `ORC-STAKE-LP-COMPOUND` are similar strategies but use Saber and Orca protocols.
|
||||
|
||||
## Initialization
|
||||
|
||||
In order to run Vaults, first, build and deploy Farm programs and upload metadata as described in the [Quick Start Guide](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/quick_start.md).
|
||||
|
||||
To initialize and enable Vaults, run:
|
||||
|
||||
```sh
|
||||
solana-farm-ctrl vault-init all
|
||||
solana-farm-ctrl vault-enable-deposits all
|
||||
solana-farm-ctrl vault-enable-withdrawals all
|
||||
```
|
||||
|
||||
You can set Vault fees and minimum allowed crank interval with `vault-set-fee` and `vault-set-min-crank-interval`. Cranks need to be executed periodically using `solana-farm-ctrl vault-crank all` or `solana-farm-client crank-vaults`. Alternatively you can use [HTTP Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/http_client.md) or [Rust Client](https://github.com/solana-labs/solana-program-library/blob/master/farms/docs/rust_client.md) or send raw instructions.
|
||||
|
||||
Whether the Vault is properly initialized can be verified with client tools, e.g., `solana-farm-client vault-info [VAULT_NAME]`.
|
|
@ -1,2 +0,0 @@
|
|||
/target
|
||||
Cargo.lock
|
|
@ -1,48 +0,0 @@
|
|||
[package]
|
||||
name = "solana-farm-client"
|
||||
version = "1.1.3"
|
||||
description = "Solana Farm Client"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana-program-library/farms"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
debug = []
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.16"
|
||||
arrayvec = "0.7.2"
|
||||
arrayref = "0.3.6"
|
||||
serde = "1.0.136"
|
||||
clap = "2.34.0"
|
||||
thiserror = "1.0.30"
|
||||
bs58 = "0.4.0"
|
||||
solana-sdk = "1.9.18"
|
||||
solana-client = "1.9.18"
|
||||
solana-cli-config = "1.9.18"
|
||||
solana-account-decoder = "1.9.18"
|
||||
solana-logger = "1.9.18"
|
||||
solana-version = "1.9.18"
|
||||
solana-farm-sdk = "1.1.3"
|
||||
solana-clap-utils = "1.9.18"
|
||||
spl-token = { version = "3.2.0", features = ["no-entrypoint"] }
|
||||
spl-associated-token-account = { version = "=1.0.3", features = ["no-entrypoint"] }
|
||||
spl-governance = { version = "2.1.4", features = ["no-entrypoint"] }
|
||||
quarry-mine = { version = "5.0.2", features = ["no-entrypoint"] }
|
||||
stable-swap-client = "1.8.1"
|
||||
stable-swap-math = "1.8.1"
|
||||
chrono = "0.4.19"
|
||||
base64 = "0.13.0"
|
||||
bincode = "1.3.3"
|
||||
num_enum = "0.5.7"
|
||||
pyth-client = "=0.5.0"
|
||||
|
||||
[lib]
|
||||
name = "solana_farm_client"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "solana-farm-client"
|
||||
path = "src/cli/main.rs"
|
|
@ -1 +0,0 @@
|
|||
../docs/crate.md
|
|
@ -1,61 +0,0 @@
|
|||
//! Farm Client's cache to reduce the load on RPC endpoint.
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
pub const RELOAD_INTERVAL: Duration = Duration::from_secs(21600);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Cache<T> {
|
||||
pub data: HashMap<String, T>,
|
||||
pub last_load: SystemTime,
|
||||
pub counter: u32,
|
||||
}
|
||||
|
||||
impl<T> Default for Cache<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
data: HashMap::<String, T>::new(),
|
||||
last_load: SystemTime::now() - RELOAD_INTERVAL,
|
||||
counter: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Cache<T> {
|
||||
pub fn is_stale(&self) -> bool {
|
||||
if self.data.is_empty() {
|
||||
return true;
|
||||
}
|
||||
if let Ok(diff) = SystemTime::now().duration_since(self.last_load) {
|
||||
return diff >= RELOAD_INTERVAL;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.data.is_empty()
|
||||
}
|
||||
|
||||
pub fn is_updated(&self, counter: u32) -> bool {
|
||||
counter != self.counter
|
||||
}
|
||||
|
||||
pub fn set(&mut self, data: HashMap<String, T>, counter: u32) {
|
||||
self.data = data;
|
||||
self.last_load = SystemTime::now();
|
||||
self.counter = counter;
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.data = HashMap::<String, T>::new();
|
||||
self.last_load = SystemTime::now().checked_sub(RELOAD_INTERVAL).unwrap();
|
||||
self.counter = 0;
|
||||
}
|
||||
|
||||
pub fn mark_not_stale(&mut self) {
|
||||
self.last_load = SystemTime::now();
|
||||
}
|
||||
}
|
|
@ -1,934 +0,0 @@
|
|||
//! Configuration and command line arguments management.
|
||||
|
||||
use {
|
||||
clap::{crate_description, crate_name, App, AppSettings, Arg, ArgMatches, SubCommand},
|
||||
solana_clap_utils::{input_validators::is_url, keypair::signer_from_path},
|
||||
solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey, signature::Signer},
|
||||
std::str::FromStr,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Config {
|
||||
pub farm_client_url: String,
|
||||
pub commitment: CommitmentConfig,
|
||||
pub keypair: Box<dyn Signer>,
|
||||
pub no_pretty_print: bool,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new(matches: &ArgMatches) -> Self {
|
||||
let cli_config = if let Some(config_file) = matches.value_of("config_file") {
|
||||
match solana_cli_config::Config::load(config_file) {
|
||||
Err(e) => {
|
||||
panic!("Failed to load config file \"{}\":{}", config_file, e);
|
||||
}
|
||||
Ok(config) => config,
|
||||
}
|
||||
} else {
|
||||
solana_cli_config::Config::default()
|
||||
};
|
||||
|
||||
let farm_client_url = matches
|
||||
.value_of("farm_client_url")
|
||||
.unwrap_or(&cli_config.json_rpc_url);
|
||||
let keypair_path = matches
|
||||
.value_of("keypair")
|
||||
.unwrap_or(&cli_config.keypair_path);
|
||||
let commitment = matches
|
||||
.value_of("commitment")
|
||||
.unwrap_or(&cli_config.commitment);
|
||||
|
||||
Self {
|
||||
farm_client_url: farm_client_url.to_string(),
|
||||
commitment: CommitmentConfig::from_str(commitment).unwrap(),
|
||||
keypair: signer_from_path(matches, keypair_path, "signer", &mut None).unwrap(),
|
||||
no_pretty_print: matches.is_present("no_pretty_print"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_target(matches: &ArgMatches) -> String {
|
||||
matches
|
||||
.value_of("target")
|
||||
.unwrap()
|
||||
.parse::<String>()
|
||||
.unwrap()
|
||||
.to_lowercase()
|
||||
}
|
||||
|
||||
pub fn get_str_val<'a>(matches: &ArgMatches<'a>, argname: &str) -> String {
|
||||
matches
|
||||
.value_of(argname)
|
||||
.unwrap()
|
||||
.parse::<String>()
|
||||
.unwrap()
|
||||
.to_uppercase()
|
||||
}
|
||||
|
||||
pub fn get_str_val_raw<'a>(matches: &ArgMatches<'a>, argname: &str) -> String {
|
||||
matches
|
||||
.value_of(argname)
|
||||
.unwrap()
|
||||
.parse::<String>()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn get_vec_str_val<'a>(matches: &ArgMatches<'a>, argname: &str) -> Vec<String> {
|
||||
matches
|
||||
.value_of(argname)
|
||||
.unwrap()
|
||||
.parse::<String>()
|
||||
.unwrap()
|
||||
.to_uppercase()
|
||||
.split(',')
|
||||
.collect::<Vec<&str>>()
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_vec_str_val_raw<'a>(matches: &ArgMatches<'a>, argname: &str) -> Vec<String> {
|
||||
matches
|
||||
.value_of(argname)
|
||||
.unwrap()
|
||||
.parse::<String>()
|
||||
.unwrap()
|
||||
.split(',')
|
||||
.collect::<Vec<&str>>()
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_pubkey_val<'a>(matches: &ArgMatches<'a>, argname: &str) -> Pubkey {
|
||||
Pubkey::from_str(matches.value_of(argname).unwrap()).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_integer_val<'a>(matches: &ArgMatches<'a>, argname: &str) -> u64 {
|
||||
matches.value_of(argname).unwrap().parse::<u64>().unwrap()
|
||||
}
|
||||
|
||||
pub fn get_floating_val<'a>(matches: &ArgMatches<'a>, argname: &str) -> f64 {
|
||||
matches.value_of(argname).unwrap().parse::<f64>().unwrap()
|
||||
}
|
||||
|
||||
fn get_arg(name: &str) -> Arg {
|
||||
Arg::with_name(name).required(true).takes_value(true)
|
||||
}
|
||||
|
||||
fn get_integer_arg(name: &str) -> Arg {
|
||||
Arg::with_name(name)
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(|p| match p.parse::<u64>() {
|
||||
Err(_) => Err(String::from("Must be unsigned integer")),
|
||||
Ok(_) => Ok(()),
|
||||
})
|
||||
}
|
||||
|
||||
fn get_floating_arg(name: &str) -> Arg {
|
||||
Arg::with_name(name)
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(|p| match p.parse::<f64>() {
|
||||
Err(_) => Err(String::from("Must be floating number")),
|
||||
Ok(_) => Ok(()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_clap_app<'a, 'b>(version: &'b str) -> App<'a, 'b> {
|
||||
let target = Arg::with_name("target")
|
||||
.required(true)
|
||||
.takes_value(true)
|
||||
.possible_values(&["program", "vault", "farm", "pool", "token", "fund"])
|
||||
.hide_possible_values(true)
|
||||
.help("Target object type (program, vault, etc.)");
|
||||
|
||||
let objectname = Arg::with_name("object_name")
|
||||
.required(true)
|
||||
.takes_value(true)
|
||||
.help("Target object name");
|
||||
|
||||
let tokenname = Arg::with_name("token_name")
|
||||
.required(true)
|
||||
.takes_value(true)
|
||||
.help("Token name");
|
||||
|
||||
let tokenname2 = Arg::with_name("token_name2")
|
||||
.required(true)
|
||||
.takes_value(true)
|
||||
.help("Second token name");
|
||||
|
||||
let amount = Arg::with_name("amount")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(|p| match p.parse::<f64>() {
|
||||
Err(_) => Err(String::from("Must be unsigned decimal")),
|
||||
Ok(val) => {
|
||||
if val >= 0.0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(String::from("Must be unsigned decimal"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.help("Token amount");
|
||||
|
||||
let amount2 = Arg::with_name("amount2")
|
||||
.takes_value(true)
|
||||
.required(false)
|
||||
.default_value("0")
|
||||
.validator(|p| match p.parse::<f64>() {
|
||||
Err(_) => Err(String::from("Must be unsigned decimal")),
|
||||
Ok(val) => {
|
||||
if val >= 0.0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(String::from("Must be unsigned decimal"))
|
||||
}
|
||||
}
|
||||
})
|
||||
.help("Second token amount");
|
||||
|
||||
let wallet = Arg::with_name("wallet")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(|p| match Pubkey::from_str(&p) {
|
||||
Err(_) => Err(String::from("Must be public key")),
|
||||
Ok(_) => Ok(()),
|
||||
})
|
||||
.help("Wallet address");
|
||||
|
||||
App::new(crate_name!())
|
||||
.about(crate_description!())
|
||||
.version(version)
|
||||
.arg(
|
||||
Arg::with_name("log_level")
|
||||
.short("L")
|
||||
.long("log-level")
|
||||
.takes_value(true)
|
||||
.default_value("info")
|
||||
.global(true)
|
||||
.help("Log verbosity level")
|
||||
.possible_values(&[
|
||||
"debug", "info", "warning", "error"
|
||||
])
|
||||
.hide_possible_values(false)
|
||||
)
|
||||
.arg({
|
||||
let arg = Arg::with_name("config_file")
|
||||
.short("C")
|
||||
.long("config")
|
||||
.takes_value(true)
|
||||
.global(true)
|
||||
.help("Configuration file to use");
|
||||
if let Some(ref config_file) = *solana_cli_config::CONFIG_FILE {
|
||||
arg.default_value(config_file)
|
||||
} else {
|
||||
arg
|
||||
}
|
||||
})
|
||||
.arg(
|
||||
Arg::with_name("farm_client_url")
|
||||
.short("f")
|
||||
.long("farm-client-url")
|
||||
.takes_value(true)
|
||||
.global(true)
|
||||
.validator(is_url)
|
||||
.help("RPC URL to use with Farm Client"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("keypair")
|
||||
.short("k")
|
||||
.long("keypair")
|
||||
.global(true)
|
||||
.takes_value(true)
|
||||
.help("Filepath or URL to a keypair"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("commitment")
|
||||
.long("commitment")
|
||||
.short("c")
|
||||
.takes_value(true)
|
||||
.possible_values(&[
|
||||
"processed",
|
||||
"confirmed",
|
||||
"finalized",
|
||||
])
|
||||
.hide_possible_values(false)
|
||||
.global(true)
|
||||
.help("Return information at the selected commitment level"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("no_pretty_print")
|
||||
.short("n")
|
||||
.long("no-pretty-print")
|
||||
.global(true)
|
||||
.takes_value(false)
|
||||
.help("Print every record in one line"),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("get")
|
||||
.about("Query specified object in blockchain and print")
|
||||
.arg(target.clone())
|
||||
.arg(objectname.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("get-ref")
|
||||
.about("Query specified object by reference address and print")
|
||||
.arg(target.clone())
|
||||
.arg(objectname.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("get-all")
|
||||
.about("Query all objects of the given type and print")
|
||||
.arg(target.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("list-all")
|
||||
.about("Query all object names of the given type and print")
|
||||
.arg(target.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("protocols")
|
||||
.about("Print description and stats of all supported protocols")
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("pool-price")
|
||||
.about("Print pool price")
|
||||
.arg(get_arg("pool_name")),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("oracle-price")
|
||||
.about("Print oracle price")
|
||||
.arg(get_arg("symbol"))
|
||||
.arg(get_integer_arg("max_price_age_sec"))
|
||||
.arg(get_floating_arg("max_price_error")),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("transfer")
|
||||
.about("Transfer SOL to another wallet")
|
||||
.arg(wallet.clone())
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("token-transfer")
|
||||
.about("Transfer tokens to another wallet")
|
||||
.arg(tokenname.clone())
|
||||
.arg(wallet.clone())
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("wrap-sol")
|
||||
.about("Transfer SOL to the associated WSOL account")
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("unwrap-sol")
|
||||
.about("Transfer WSOL back to SOL by closing ATA")
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("sync-token-balance")
|
||||
.about("Updates token balance of the account")
|
||||
.arg(tokenname.clone())
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("token-address")
|
||||
.about("Print associated token account address")
|
||||
.arg(tokenname.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("token-data")
|
||||
.about("Print token account metadata")
|
||||
.arg(tokenname.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("token-create")
|
||||
.about("Create associated token account")
|
||||
.arg(tokenname.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("token-close")
|
||||
.about("Close associated token account")
|
||||
.arg(tokenname.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("token-supply")
|
||||
.about("Print token supply")
|
||||
.arg(tokenname.clone()),
|
||||
)
|
||||
.subcommand(SubCommand::with_name("balance").about("Print SOL balance"))
|
||||
.subcommand(
|
||||
SubCommand::with_name("token-balance")
|
||||
.about("Print token balance")
|
||||
.arg(tokenname.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("stake-balance")
|
||||
.about("Print user's stake balance in the farm")
|
||||
.arg(get_arg("farm_name")),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("wallet-balances")
|
||||
.about("Print all token balances for the wallet")
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("vault-info")
|
||||
.about("Print vault stats")
|
||||
.arg(get_arg("vault_name")),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("vault-user-info")
|
||||
.about("Print user stats for the vault")
|
||||
.arg(get_arg("vault_name")),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("fund-info")
|
||||
.about("Print fund stats")
|
||||
.arg(get_arg("fund_name")),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("fund-user-info")
|
||||
.about("Print user stats for the fund")
|
||||
.arg(get_arg("fund_name"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("fund-user-requests")
|
||||
.about("Print user requests for the fund")
|
||||
.arg(get_arg("fund_name"))
|
||||
.arg(tokenname.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("fund-assets")
|
||||
.about("Print fund assets info")
|
||||
.arg(get_arg("fund_name"))
|
||||
.arg(get_arg("asset_type"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("fund-custody")
|
||||
.about("Print fund custody info")
|
||||
.arg(get_arg("fund_name"))
|
||||
.arg(tokenname.clone())
|
||||
.arg(get_arg("custody_type"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("fund-custodies")
|
||||
.about("Print all fund custodies")
|
||||
.arg(get_arg("fund_name"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("fund-vault")
|
||||
.about("Print fund vault info")
|
||||
.arg(get_arg("fund_name"))
|
||||
.arg(get_arg("vault_name"))
|
||||
.arg(get_arg("vault_type"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("fund-vaults")
|
||||
.about("Print all fund vaults")
|
||||
.arg(get_arg("fund_name"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("find-funds")
|
||||
.about("Find all Funds with Vault names matching given pattern")
|
||||
.arg(get_arg("vault_name_pattern"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("find-pools")
|
||||
.about("Find all Pools with tokens A and B")
|
||||
.arg(get_arg("protocol"))
|
||||
.arg(tokenname.clone())
|
||||
.arg(tokenname2.clone())
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("find-pools-with-lp")
|
||||
.about("Find all Pools for the given LP token")
|
||||
.arg(tokenname.clone())
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("find-farms-with-lp")
|
||||
.about("Find all Farms for the given LP token")
|
||||
.arg(tokenname.clone())
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("find-vaults")
|
||||
.about("Find all Vaults with tokens A and B")
|
||||
.arg(tokenname.clone())
|
||||
.arg(tokenname2.clone())
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("find-vaults-with-vt")
|
||||
.about("Find all Vaults for the given VT token")
|
||||
.arg(tokenname.clone())
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("swap")
|
||||
.about("Swap tokens in the pool")
|
||||
.arg(get_arg("protocol"))
|
||||
.arg(tokenname.clone())
|
||||
.arg(tokenname2.clone())
|
||||
.arg(amount.clone())
|
||||
.arg(amount2.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("deposit-pool")
|
||||
.about("Add liquidity to the pool")
|
||||
.arg(get_arg("pool_name"))
|
||||
.arg(amount.clone())
|
||||
.arg(amount2.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("withdraw-pool")
|
||||
.about("Remove liquidity from the pool")
|
||||
.arg(get_arg("pool_name"))
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("stake")
|
||||
.about("Stake LP tokens to the farm")
|
||||
.arg(get_arg("farm_name"))
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("harvest")
|
||||
.about("Harvest farm rewards")
|
||||
.arg(get_arg("farm_name")),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("unstake")
|
||||
.about("Unstake LP tokens from the farm")
|
||||
.arg(get_arg("farm_name"))
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("deposit-vault")
|
||||
.about("Add liquidity to the vault")
|
||||
.arg(get_arg("vault_name"))
|
||||
.arg(amount.clone())
|
||||
.arg(amount2.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("deposit-vault-locked")
|
||||
.about("Add locked liquidity to the vault")
|
||||
.arg(get_arg("vault_name"))
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("withdraw-vault")
|
||||
.about("Remove liquidity from the vault")
|
||||
.arg(get_arg("vault_name"))
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("withdraw-vault-unlocked")
|
||||
.about("Remove unlocked liquidity from the vault")
|
||||
.arg(get_arg("vault_name"))
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("crank-vault")
|
||||
.about("Crank single vault")
|
||||
.arg(get_arg("vault_name"))
|
||||
.arg(get_integer_arg("step"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("crank-vaults")
|
||||
.about("Crank all vaults")
|
||||
.arg(get_integer_arg("step"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("request-deposit-fund")
|
||||
.about("Request a new deposit to the fund")
|
||||
.arg(get_arg("fund_name"))
|
||||
.arg(tokenname.clone())
|
||||
.arg(amount.clone())
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("cancel-deposit-fund")
|
||||
.about("Cancel pending deposit to the Fund")
|
||||
.arg(get_arg("fund_name"))
|
||||
.arg(tokenname.clone())
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("request-withdrawal-fund")
|
||||
.about("Request a new withdrawal from the fund")
|
||||
.arg(get_arg("fund_name"))
|
||||
.arg(tokenname.clone())
|
||||
.arg(amount.clone())
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("cancel-withdrawal-fund")
|
||||
.about("Cancel pending withdrawal from the Fund")
|
||||
.arg(get_arg("fund_name"))
|
||||
.arg(tokenname.clone())
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("start-liquidation-fund")
|
||||
.about("Start the Fund liquidation")
|
||||
.arg(get_arg("fund_name"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("update-fund-assets-with-custody")
|
||||
.about("Update fund assets info based on custody holdings")
|
||||
.arg(get_arg("fund_name"))
|
||||
.arg(get_integer_arg("custody_id"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("update-fund-assets-with-custodies")
|
||||
.about("Update fund assets info based on all custodies")
|
||||
.arg(get_arg("fund_name"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("update-fund-assets-with-vault")
|
||||
.about("Update fund assets info based on vault holdings")
|
||||
.arg(get_arg("fund_name"))
|
||||
.arg(get_integer_arg("vault_id"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("update-fund-assets-with-vaults")
|
||||
.about("Update fund assets info based on all vaults")
|
||||
.arg(get_arg("fund_name"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("governance")
|
||||
.about("Governance commands. See `solana-farm-client governance help`")
|
||||
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.subcommand(
|
||||
SubCommand::with_name("get-config")
|
||||
.about("Get governance config")
|
||||
.arg(get_arg("governance_name"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("get-address")
|
||||
.about("Get governance account address")
|
||||
.arg(get_arg("governance_name"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("get-instruction")
|
||||
.about("Print stored instruction in the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("custody-new")
|
||||
.about("Create new token custody account")
|
||||
.arg(get_arg("token_name"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("tokens-deposit")
|
||||
.about("Deposit governing tokens")
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("tokens-withdraw")
|
||||
.about("Withdraw governing tokens")
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("proposal-new")
|
||||
.about("Create a new proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_arg("proposal_name"))
|
||||
.arg(get_arg("proposal_link"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("proposal-cancel")
|
||||
.about("Cancel the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("proposal-state")
|
||||
.about("Get proposal state")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("signatory-add")
|
||||
.about("Add a signatory to the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_arg("signatory"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("signatory-remove")
|
||||
.about("Remove the signatory from the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_arg("signatory"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("sign-off")
|
||||
.about("Sign off the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("vote-cast")
|
||||
.about("Cast a vote on the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("vote"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("vote-relinquish")
|
||||
.about("Remove the vote from the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("vote-finalize")
|
||||
.about("Finalize the vote on the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-execute")
|
||||
.about("Execute the instruction in the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-flag-error")
|
||||
.about("Mark the instruction as failed")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-remove")
|
||||
.about("Remove the instruction from the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-insert")
|
||||
.about("Add a new custom instruction to the proposal. Must be serialized with base64::encode(bincode::serialize(&inst).unwrap().as_slice())")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("base64_instruction"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-verify")
|
||||
.about("Verify custom instruction in the proposal. Must be serialized with base64::encode(bincode::serialize(&inst).unwrap().as_slice())")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("base64_instruction"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-insert-token-transfer")
|
||||
.about("Add a new token transfer instruction to the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(tokenname.clone())
|
||||
.arg(wallet.clone())
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-verify-token-transfer")
|
||||
.about("Verify that instruction in the proposal is a token transfer")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(tokenname.clone())
|
||||
.arg(wallet.clone())
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-insert-swap")
|
||||
.about("Add a new swap instruction to the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("protocol"))
|
||||
.arg(tokenname.clone())
|
||||
.arg(tokenname2.clone())
|
||||
.arg(amount.clone())
|
||||
.arg(amount2.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-verify-swap")
|
||||
.about("Verify that instruction in the proposal is a swap")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("protocol"))
|
||||
.arg(tokenname.clone())
|
||||
.arg(tokenname2.clone())
|
||||
.arg(amount.clone())
|
||||
.arg(amount2.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-insert-deposit-pool")
|
||||
.about("Add a new add liquidity to the pool instruction to the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("pool_name"))
|
||||
.arg(amount.clone())
|
||||
.arg(amount2.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-verify-deposit-pool")
|
||||
.about("Verify that instruction in the proposal is an add liquidity to the pool")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("pool_name"))
|
||||
.arg(amount.clone())
|
||||
.arg(amount2.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-insert-withdraw-pool")
|
||||
.about("Add a new remove liquidity from the pool instruction to the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("pool_name"))
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-verify-withdraw-pool")
|
||||
.about("Verify that instruction in the proposal is a remove liquidity from the pool")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("pool_name"))
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-insert-stake")
|
||||
.about("Add a new stake instruction to the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("farm_name"))
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-verify-stake")
|
||||
.about("Verify that instruction in the proposal is a stake")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("farm_name"))
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-insert-harvest")
|
||||
.about("Add a new harvest instruction to the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("farm_name")),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-verify-harvest")
|
||||
.about("Verify that instruction in the proposal is a harvest")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("farm_name")),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-insert-unstake")
|
||||
.about("Add a new unstake instruction to the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("farm_name"))
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-verify-unstake")
|
||||
.about("Verify that instruction in the proposal is an unstake")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("farm_name"))
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-insert-deposit-vault")
|
||||
.about("Add a new add liquidity to the vault instruction to the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("vault_name"))
|
||||
.arg(amount.clone())
|
||||
.arg(amount2.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-verify-deposit-vault")
|
||||
.about("Verify that instruction in the proposal is an add liquidity to the vault")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("vault_name"))
|
||||
.arg(amount.clone())
|
||||
.arg(amount2.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-insert-withdraw-vault")
|
||||
.about("Add a new remove liquidity from the vault instruction to the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("vault_name"))
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-verify-withdraw-vault")
|
||||
.about("Verify that instruction in the proposal is a remove liquidity from the vault")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("vault_name"))
|
||||
.arg(amount.clone()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-insert-withdraw-fees-vault")
|
||||
.about("Add a new withdraw fees from the vault instruction to the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("vault_name"))
|
||||
.arg(get_integer_arg("fee_token"))
|
||||
.arg(amount.clone())
|
||||
.arg(get_arg("receiver"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-verify-withdraw-fees-vault")
|
||||
.about("Verify that instruction in the proposal is a withdraw fees from the vault")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("vault_name"))
|
||||
.arg(get_integer_arg("fee_token"))
|
||||
.arg(amount.clone())
|
||||
.arg(get_arg("receiver"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-insert-program-upgrade")
|
||||
.about("Add a new program upgrade instruction to the proposal")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("buffer_address"))
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("instruction-verify-program-upgrade")
|
||||
.about("Verify that instruction in the proposal is a program upgrade")
|
||||
.arg(get_arg("governance_name"))
|
||||
.arg(get_integer_arg("proposal_index"))
|
||||
.arg(get_integer_arg("instruction_index"))
|
||||
.arg(get_arg("buffer_address"))
|
||||
)
|
||||
)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue