Compare commits
111 Commits
205e9d875f
...
a97d04ab06
Author | SHA1 | Date |
---|---|---|
Christian Kamm | a97d04ab06 | |
Henry-E | e910d44b67 | |
Jean Marchand (Exotic Markets) | 38bbb21c33 | |
Henry-E | 7236c8bb69 | |
riordanp | 8856aee7df | |
Henry-E | 4bef8c40ef | |
acheron | ba5b55d7f0 | |
Henry-E | 9f3d66ff8c | |
Henry-E | 347c225a27 | |
Henry-E | b00fe70d1b | |
Henry-E | f79f9dab74 | |
Jean Marchand (Exotic Markets) | fad05805d8 | |
Adithya Narayan | b662ff1460 | |
Henry-E | 03eff348db | |
Petr Kozorezov | 7527033c7b | |
Sammy Harris | 484628070c | |
Henry-E | d88a09dbb7 | |
Henry-E | 09b829d1a3 | |
Henry-E | 7d7747cc90 | |
skrrb | 6814ce646e | |
Tommy Johnson | c798821947 | |
metta0714 | 04b988299a | |
Nicolas Bayle | 57434279fd | |
Henry-E | b6ad53f628 | |
Henry-E | 1bb1969d60 | |
Italo Casas | 11af9a6624 | |
Pierre | 98f26ce8cf | |
Nicholas Clarke | 66e45327b9 | |
skrrb | fb714b9343 | |
Lucas Steuernagel | 0ba2195ec7 | |
Kyle Gilliam | 23c9717af1 | |
surfertas | 50724df110 | |
acheron | bbeffd58f7 | |
Henry-E | 66e4295fe0 | |
Matthew Callens | 2768112cef | |
Matthew Callens | fa2f18a2c0 | |
Henry-E | 35f4e26ef8 | |
Darren | 704fdce0cd | |
Jean Marchand (Exotic Markets) | ca7c3f0aca | |
Henry-E | 19e00e5d8f | |
cyphersnake | a73bd728fb | |
Henry-E | 686c97e45b | |
Jean Marchand (Exotic Markets) | ca52121a6b | |
Arjun | 52b32d2459 | |
Jean Marchand (Exotic Markets) | c0d3ff2d77 | |
Matthew Callens | be67192f2b | |
Jean Marchand (Exotic Markets) | b8cda8a717 | |
Henry-E | 5291a7b6ff | |
Henry-E | afbbbb0330 | |
Callum McIntyre | 982799b7e6 | |
Jac0xb | 1ec2b7dfcd | |
Proph3t | d441a3e6de | |
Jean Marchand (Exotic Markets) | 89b047b21e | |
Luis Perrone | fb6508addf | |
Jean Marchand (Exotic Markets) | 8ce18c36db | |
Matthew Callens | 5e3ebcfde3 | |
Tracy Adams | ee026aa8f5 | |
acheron | af115999c6 | |
filipzeta | 5c474c6dfb | |
Jean Marchand | b107cbe94f | |
Deep Mehta | e8618a588f | |
omahs | 43ab991852 | |
Noah Prince | feff131ab0 | |
Noah Prince | 91a2b7ec96 | |
Henry-E | 7a0a37e0f8 | |
Proph3t | 5532a0f087 | |
Sammy Harris | fa1249836e | |
Henry-E | 46f0b67bff | |
Vipin Saini | 6a46bedaf1 | |
Sammy Harris | d653226f40 | |
Sammy Harris | e866d45af9 | |
Dana | 8719facf60 | |
Totoro | 8e66d5bb5f | |
Ryan Mehta | 0f10a99995 | |
eydelrivero | b9b2737c18 | |
Sammy Harris | 3da28dbc5f | |
Sammy Harris | 5647510615 | |
Henry-E | d83efcdc3a | |
Noah Prince | 52a7e1e278 | |
Noah Prince | fd467df932 | |
Henry-E | 862575a649 | |
Han Yang | 3970847e75 | |
stateofnonreturn | 7f243ce66c | |
Noah Prince | 6f3877f36c | |
Noah Prince | d929589013 | |
Noah Prince | 436791b039 | |
Gajesh Naik | 2a07d841c6 | |
0xWoo | e019ba2199 | |
Totoro | 5ae52cae13 | |
Matthew Callens | f155996c3c | |
Matthew Callens | 4c24c5d7b1 | |
Noah Prince | e69e50daaf | |
surfertas | 0c70d183ef | |
Pierre | 8ee4600785 | |
Sammy Harris | c1c4f6c335 | |
Matthew Callens | 586094156b | |
acheron | 5a025b949e | |
dependabot[bot] | a59aa5f6f3 | |
dependabot[bot] | 7bdbb03cbd | |
Sammy Harris | 290b2aa43e | |
tomland123 | fc9c680d6a | |
Sammy Harris | 3a0deba901 | |
chinepun | bf11f811d7 | |
Matthew Callens | 7101ece803 | |
Armani Ferrante | e134cce3f3 | |
Kevin Rodríguez | d3ee9b529a | |
acheron | 9457180a0a | |
Matthew Callens | 000e74ed48 | |
Luis Cossío | ebe88187e3 | |
Michal Rostecki | 93332766f1 | |
Sammy Harris | 68362aca58 |
|
@ -0,0 +1 @@
|
|||
large-error-threshold = 1_000_000
|
|
@ -3,19 +3,27 @@ description: "Setup Solana"
|
|||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: actions/cache@v2
|
||||
name: Cache Solana Tool Suite
|
||||
id: cache-solana
|
||||
with:
|
||||
path: |
|
||||
~/.cache/solana/
|
||||
~/.local/share/solana/
|
||||
key: solana-${{ runner.os }}-v0000-${{ env.SOLANA_CLI_VERSION }}
|
||||
- run: sh -c "$(curl -sSfL https://release.solana.com/v${{ env.SOLANA_CLI_VERSION }}/install)"
|
||||
shell: bash
|
||||
- run: echo "/home/runner/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH
|
||||
shell: bash
|
||||
- run: solana-keygen new --no-bip39-passphrase
|
||||
shell: bash
|
||||
- run: solana config set --url localhost
|
||||
- uses: actions/cache@v3
|
||||
name: Cache Solana Tool Suite
|
||||
id: cache-solana
|
||||
with:
|
||||
path: |
|
||||
~/.cache/solana/
|
||||
~/.local/share/solana/
|
||||
key: solana-${{ runner.os }}-v0000-${{ env.SOLANA_CLI_VERSION }}
|
||||
- uses: nick-fields/retry@v2
|
||||
with:
|
||||
retry_wait_seconds: 300
|
||||
timeout_minutes: 2
|
||||
max_attempts: 10
|
||||
retry_on: error
|
||||
shell: bash
|
||||
command: sh -c "$(curl -sSfL https://release.solana.com/v${{ env.SOLANA_CLI_VERSION }}/install)"
|
||||
- run: sh -c "$(curl -sSfL https://release.solana.com/v${{ env.SOLANA_CLI_VERSION }}/install)"
|
||||
shell: bash
|
||||
- run: echo "/home/runner/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH
|
||||
shell: bash
|
||||
- run: solana-keygen new --no-bip39-passphrase
|
||||
shell: bash
|
||||
- run: solana config set --url localhost
|
||||
shell: bash
|
||||
|
|
|
@ -3,27 +3,32 @@ description: "Setup ts"
|
|||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
- uses: actions/cache@v2
|
||||
name: Cache Typescript node_modules
|
||||
id: cache-typescript-node-modules
|
||||
with:
|
||||
path: |
|
||||
./ts/node_modules/
|
||||
key: solana-${{ runner.os }}-v0000-${{ env.NODE_VERSION }}-${{ hashFiles('./ts/**/yarn.lock') }}
|
||||
- uses: actions/cache@v2
|
||||
name: Cache Typescript Dist
|
||||
id: cache-typescript-dist
|
||||
with:
|
||||
path: |
|
||||
./ts/dist/
|
||||
key: solana-${{ runner.os }}-v0000-${{ env.NODE_VERSION }}-${{ hashFiles('./ts/**/*.ts') }}
|
||||
- run: cd ts && yarn --frozen-lockfile && yarn build:node && yarn link && cd ../
|
||||
shell: bash
|
||||
- run: cd examples/tutorial && yarn link @project-serum/anchor && yarn --frozen-lockfile && cd ../../
|
||||
shell: bash
|
||||
- run: cd tests && yarn link @project-serum/anchor && yarn --frozen-lockfile && cd ..
|
||||
shell: bash
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
- uses: actions/cache@v3
|
||||
name: Cache Typescript node_modules
|
||||
id: cache-typescript-node-modules
|
||||
with:
|
||||
path: |
|
||||
./ts/node_modules/
|
||||
key: solana-${{ runner.os }}-v0000-${{ env.NODE_VERSION }}-${{ hashFiles('./ts/**/yarn.lock') }}
|
||||
- uses: actions/cache@v3
|
||||
name: Cache Typescript Dist
|
||||
id: cache-typescript-dist
|
||||
with:
|
||||
path: |
|
||||
./ts/dist/
|
||||
key: solana-${{ runner.os }}-v0000-${{ env.NODE_VERSION }}-${{ hashFiles('./ts/**/*.ts') }}
|
||||
- run: cd ts/packages/borsh && yarn --frozen-lockfile && yarn build && cd ../../../
|
||||
shell: bash
|
||||
- run: cd ts/packages/anchor && yarn --frozen-lockfile && yarn build:node && yarn link && cd ../../../
|
||||
shell: bash
|
||||
- run: cd ts/packages/spl-associated-token-account && yarn --frozen-lockfile && yarn build:node && yarn link && cd ../../../
|
||||
shell: bash
|
||||
- run: cd ts/packages/spl-token && yarn --frozen-lockfile && yarn build:node && yarn link && cd ../../../
|
||||
shell: bash
|
||||
- run: cd examples/tutorial && yarn link @coral-xyz/anchor && yarn --frozen-lockfile && cd ../../
|
||||
shell: bash
|
||||
- run: cd tests && yarn link @coral-xyz/anchor && yarn link @coral-xyz/spl-associated-token-account && yarn link @coral-xyz/spl-token && yarn --frozen-lockfile && cd ..
|
||||
shell: bash
|
||||
|
|
|
@ -5,18 +5,18 @@ on:
|
|||
branches:
|
||||
- master
|
||||
env:
|
||||
SOLANA_CLI_VERSION: 1.10.29
|
||||
SOLANA_CLI_VERSION: 1.14.11
|
||||
NODE_VERSION: 17.0.1
|
||||
|
||||
jobs:
|
||||
test-core:
|
||||
name: Core Tests
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup/
|
||||
- uses: actions/setup-node@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
- run: cargo build
|
||||
|
@ -25,9 +25,11 @@ jobs:
|
|||
- run: cargo test
|
||||
# using singlethreaded testing for avm so that tests that change files do not conflict with each other
|
||||
- run: cd avm && cargo fmt -- --check && cargo clippy --all-targets -- -D warnings && cargo test -- --test-threads=1
|
||||
- run: cd ts && yarn --frozen-lockfile
|
||||
- run: cd ts && yarn test
|
||||
- run: cd ts && yarn lint
|
||||
# Init local borsh package
|
||||
- run: cd ts/packages/borsh && yarn --frozen-lockfile && yarn build
|
||||
- run: cd ts/packages/anchor && yarn --frozen-lockfile
|
||||
- run: cd ts/packages/anchor && yarn test
|
||||
- run: cd ts/packages/anchor && yarn lint
|
||||
- run: cd examples/tutorial && yarn --frozen-lockfile
|
||||
- run: cd examples/tutorial && yarn lint
|
||||
- run: cd tests && yarn --frozen-lockfile
|
||||
|
@ -36,15 +38,15 @@ jobs:
|
|||
|
||||
setup-anchor-cli:
|
||||
name: Setup Anchor cli
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup/
|
||||
|
||||
- run: cargo install --path cli anchor-cli --locked --force
|
||||
- run: chmod +x ~/.cargo/bin/anchor
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: anchor-binary-no-caching
|
||||
path: ~/.cargo/bin/anchor
|
||||
|
@ -54,11 +56,11 @@ jobs:
|
|||
test-examples:
|
||||
needs: setup-anchor-cli
|
||||
name: Examples Test
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: anchor-binary-no-caching
|
||||
path: ~/.cargo/bin/
|
||||
|
@ -73,12 +75,14 @@ jobs:
|
|||
setup-client-example:
|
||||
needs: setup-anchor-cli
|
||||
name: Setup Client Example Test
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node:
|
||||
- path: tests/optional/
|
||||
name: optional.so
|
||||
- path: tests/events/
|
||||
name: events.so
|
||||
- path: examples/tutorial/basic-4/
|
||||
|
@ -88,18 +92,18 @@ jobs:
|
|||
- path: tests/composite/
|
||||
name: composite.so
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup/
|
||||
- uses: ./.github/actions/setup-solana/
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: anchor-binary-no-caching
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +rwx ~/.cargo/bin/anchor
|
||||
|
||||
- run: cd ${{ matrix.node.path }} && anchor build --skip-lint
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.node.name }}
|
||||
path: ${{ matrix.node.path }}target/deploy/${{ matrix.node.name }}
|
||||
|
@ -108,32 +112,36 @@ jobs:
|
|||
test-client-example:
|
||||
needs: setup-client-example
|
||||
name: Client Example Test
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup/
|
||||
- uses: ./.github/actions/setup-ts/
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: anchor-binary-no-caching
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/anchor
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: optional.so
|
||||
path: tests/optional/target/deploy/
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: events.so
|
||||
path: tests/events/target/deploy/
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: basic_4.so
|
||||
path: examples/tutorial/basic-4/target/deploy/
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: basic_2.so
|
||||
path: examples/tutorial/basic-2/target/deploy/
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: composite.so
|
||||
path: tests/composite/target/deploy/
|
||||
|
@ -144,15 +152,15 @@ jobs:
|
|||
test-bpf-upgradeable-state:
|
||||
needs: setup-anchor-cli
|
||||
name: Test tests/bpf-upgradeable-state
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup/
|
||||
- uses: ./.github/actions/setup-ts/
|
||||
- uses: ./.github/actions/setup-solana/
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: anchor-binary-no-caching
|
||||
path: ~/.cargo/bin/
|
||||
|
@ -168,66 +176,66 @@ jobs:
|
|||
- run: cd tests/bpf-upgradeable-state && npx tsc --noEmit
|
||||
- uses: ./.github/actions/git-diff/
|
||||
|
||||
# this test exists to make sure that anchor
|
||||
# checks rent correctly for legacy accounts
|
||||
# that don't have to be rent-exempt
|
||||
test-misc-non-rent-exempt:
|
||||
# the anchor cli is built with a different solana version
|
||||
# but that's fine since it's just the cli
|
||||
needs: setup-anchor-cli
|
||||
name: Test tests/misc/nonRentExempt
|
||||
runs-on: ubuntu-18.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/actions/setup/
|
||||
- uses: ./.github/actions/setup-ts/
|
||||
# using an outdated validator but that
|
||||
# is ok as long as the test doesn't
|
||||
# include newer incompatible features
|
||||
- run: sh -c "$(curl -sSfL https://release.solana.com/v1.8.14/install)"
|
||||
shell: bash
|
||||
- run: echo "/home/runner/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH
|
||||
shell: bash
|
||||
- run: solana-keygen new --no-bip39-passphrase
|
||||
shell: bash
|
||||
- run: solana config set --url localhost
|
||||
shell: bash
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: anchor-binary-no-caching
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/anchor
|
||||
# # this test exists to make sure that anchor
|
||||
# # checks rent correctly for legacy accounts
|
||||
# # that don't have to be rent-exempt
|
||||
# test-misc-non-rent-exempt:
|
||||
# # the anchor cli is built with a different solana version
|
||||
# # but that's fine since it's just the cli
|
||||
# needs: setup-anchor-cli
|
||||
# name: Test tests/misc/nonRentExempt
|
||||
# runs-on: ubuntu-latest
|
||||
# timeout-minutes: 30
|
||||
# steps:
|
||||
# - uses: actions/checkout@v3
|
||||
# - uses: ./.github/actions/setup/
|
||||
# - uses: ./.github/actions/setup-ts/
|
||||
# # using an outdated validator but that
|
||||
# # is ok as long as the test doesn't
|
||||
# # include newer incompatible features
|
||||
# - run: sh -c "$(curl -sSfL https://release.solana.com/v1.8.14/install)"
|
||||
# shell: bash
|
||||
# - run: echo "/home/runner/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH
|
||||
# shell: bash
|
||||
# - run: solana-keygen new --no-bip39-passphrase
|
||||
# shell: bash
|
||||
# - run: solana config set --url localhost
|
||||
# shell: bash
|
||||
# - uses: actions/download-artifact@v3
|
||||
# with:
|
||||
# name: anchor-binary-no-caching
|
||||
# path: ~/.cargo/bin/
|
||||
# - run: chmod +x ~/.cargo/bin/anchor
|
||||
|
||||
- run: cd tests/misc && yarn --frozen-lockfile
|
||||
- run: cd tests/misc
|
||||
- run: cd tests/misc && chmod +x ci.sh && ./ci.sh
|
||||
- run: cd tests/misc && anchor test --skip-lint
|
||||
# - run: cd tests/misc && yarn --frozen-lockfile
|
||||
# - run: cd tests/misc
|
||||
# - run: cd tests/misc && chmod +x ci.sh && ./ci.sh
|
||||
# - run: cd tests/misc && anchor test --skip-lint
|
||||
|
||||
test-anchor-init:
|
||||
needs: setup-anchor-cli
|
||||
name: Test Anchor Init
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup/
|
||||
- uses: ./.github/actions/setup-ts/
|
||||
- uses: ./.github/actions/setup-solana/
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: anchor-binary-no-caching
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/anchor
|
||||
|
||||
- run: cd "$(mktemp -d)" && anchor init hello-anchor && cd hello-anchor && yarn link @project-serum/anchor && yarn && anchor test && yarn lint:fix
|
||||
- run: cd "$(mktemp -d)" && anchor init hello-anchor && cd hello-anchor && yarn link @coral-xyz/anchor && yarn && anchor test && yarn lint:fix
|
||||
- uses: ./.github/actions/git-diff/
|
||||
|
||||
test-programs:
|
||||
needs: setup-anchor-cli
|
||||
name: Test ${{ matrix.node.path }}
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
@ -243,11 +251,9 @@ jobs:
|
|||
path: spl/token-proxy
|
||||
- cmd: cd tests/multisig && anchor test --skip-lint
|
||||
path: tests/multisig
|
||||
- cmd: cd tests/interface && anchor test --skip-lint
|
||||
path: tests/interface
|
||||
- cmd: cd tests/lockup && anchor test --skip-lint
|
||||
path: tests/lockup
|
||||
- cmd: cd tests/swap/deps/serum-dex/dex && cargo build-bpf -- --locked && cd ../../../ && anchor test --skip-lint
|
||||
# - cmd: cd tests/lockup && anchor test --skip-lint
|
||||
# path: tests/lockup
|
||||
- cmd: cd tests/swap/deps/openbook-dex/dex && cargo build-bpf -- --locked && cd ../../../ && anchor test --skip-lint
|
||||
path: tests/swap
|
||||
- cmd: cd tests/escrow && anchor test --skip-lint && npx tsc --noEmit
|
||||
path: tests/escrow
|
||||
|
@ -271,8 +277,8 @@ jobs:
|
|||
path: tests/chat
|
||||
- cmd: cd tests/ido-pool && anchor test --skip-lint
|
||||
path: tests/ido-pool
|
||||
- cmd: cd tests/cfo && anchor run test-with-build && cd deps/stake && git checkout Cargo.lock && cd ../swap && git checkout Cargo.lock
|
||||
path: tests/cfo
|
||||
# - cmd: cd tests/cfo && anchor run test-with-build && cd deps/stake && git checkout Cargo.lock && cd ../swap && git checkout Cargo.lock
|
||||
# path: tests/cfo
|
||||
- cmd: cd tests/auction-house && yarn --frozen-lockfile && anchor test --skip-lint && git checkout Cargo.lock
|
||||
path: tests/auction-house
|
||||
- cmd: cd tests/floats && yarn --frozen-lockfile && anchor test --skip-lint && npx tsc --noEmit
|
||||
|
@ -287,17 +293,25 @@ jobs:
|
|||
path: tests/cpi-returns
|
||||
- cmd: cd tests/multiple-suites && anchor test --skip-lint && npx tsc --noEmit
|
||||
path: tests/multiple-suites
|
||||
- cmd: cd tests/optional && anchor test --skip-lint && npx tsc --noEmit
|
||||
path: tests/optional
|
||||
- cmd: cd tests/multiple-suites-run-single && anchor test --skip-lint --run tests/should-run && npx tsc --noEmit
|
||||
path: tests/multiple-suites-run-single
|
||||
- cmd: cd tests/pda-derivation && anchor test --skip-lint && npx tsc --noEmit
|
||||
path: tests/pda-derivation
|
||||
- cmd: cd tests/relations-derivation && anchor test --skip-lint && npx tsc --noEmit
|
||||
path: tests/relations-derivation
|
||||
- cmd: cd tests/anchor-cli-idl && ./test.sh
|
||||
path: tests/anchor-cli-idl
|
||||
- cmd: cd tests/anchor-cli-account && anchor test --skip-lint
|
||||
path: tests/anchor-cli-account
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup/
|
||||
- uses: ./.github/actions/setup-ts/
|
||||
- uses: ./.github/actions/setup-solana/
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: anchor-binary-no-caching
|
||||
path: ~/.cargo/bin/
|
||||
|
|
|
@ -8,21 +8,22 @@ on:
|
|||
branches:
|
||||
- master
|
||||
env:
|
||||
SOLANA_CLI_VERSION: 1.10.29
|
||||
SOLANA_CLI_VERSION: 1.14.11
|
||||
NODE_VERSION: 17.0.1
|
||||
CARGO_PROFILE: debug
|
||||
|
||||
jobs:
|
||||
test-core:
|
||||
name: Core Tests
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup/
|
||||
- uses: actions/setup-node@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
name: Cache Cargo registry + index
|
||||
id: cache-cargo-build
|
||||
with:
|
||||
|
@ -39,9 +40,10 @@ jobs:
|
|||
- run: cargo test
|
||||
# using singlethreaded testing for avm so that tests that change files do not conflict with each other
|
||||
- run: cd avm && cargo fmt -- --check && cargo clippy --all-targets -- -D warnings && cargo test -- --test-threads=1
|
||||
- run: cd ts && yarn --frozen-lockfile
|
||||
- run: cd ts && yarn test
|
||||
- run: cd ts && yarn lint
|
||||
- run: cd ts/packages/borsh && yarn --frozen-lockfile && yarn build
|
||||
- run: cd ts/packages/anchor && yarn --frozen-lockfile
|
||||
- run: cd ts/packages/anchor && yarn test
|
||||
- run: cd ts/packages/anchor && yarn lint
|
||||
- run: cd examples/tutorial && yarn --frozen-lockfile
|
||||
- run: cd examples/tutorial && yarn lint
|
||||
- run: cd tests && yarn --frozen-lockfile
|
||||
|
@ -50,13 +52,13 @@ jobs:
|
|||
|
||||
setup-anchor-cli:
|
||||
name: Setup Anchor cli
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup/
|
||||
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
name: Cache Cargo registry + index
|
||||
id: cache-anchor
|
||||
with:
|
||||
|
@ -66,10 +68,13 @@ jobs:
|
|||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
./target/
|
||||
key: cargo-${{ runner.os }}-anchor-${{ hashFiles('**/Cargo.lock') }}
|
||||
key: cargo-${{ runner.os }}-${{ env.CARGO_PROFILE }}-anchor-${{ hashFiles('**/Cargo.lock') }}
|
||||
- run: cargo install --path cli anchor-cli --locked --force --debug
|
||||
if: env.CARGO_PROFILE == 'debug'
|
||||
- run: cargo install --path cli anchor-cli --locked --force
|
||||
if: env.CARGO_PROFILE != 'debug'
|
||||
- run: chmod +x ~/.cargo/bin/anchor
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: anchor-binary
|
||||
path: ~/.cargo/bin/anchor
|
||||
|
@ -79,11 +84,11 @@ jobs:
|
|||
test-examples:
|
||||
needs: setup-anchor-cli
|
||||
name: Examples Test
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: anchor-binary
|
||||
path: ~/.cargo/bin/
|
||||
|
@ -92,31 +97,31 @@ jobs:
|
|||
- uses: ./.github/actions/setup/
|
||||
- uses: ./.github/actions/setup-solana/
|
||||
- uses: ./.github/actions/setup-ts/
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
name: basic-0 cache
|
||||
id: cache-basic-0
|
||||
with:
|
||||
path: ./examples/tutorial/basic-0/target
|
||||
key: cargo-${{ runner.os }}-${{ hashFiles('./examples/tutorial/basic-0/**/Cargo.toml') }}-${{ env.SOLANA_CLI_VERSION }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
name: basic-1 cache
|
||||
id: cache-basic-1
|
||||
with:
|
||||
path: ./examples/tutorial/basic-1/target
|
||||
key: cargo-${{ runner.os }}-${{ hashFiles('./examples/tutorial/basic-1/**/Cargo.toml') }}-${{ env.SOLANA_CLI_VERSION }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
name: basic-2 cache
|
||||
id: cache-basic-2
|
||||
with:
|
||||
path: ./examples/tutorial/basic-2/target
|
||||
key: cargo-${{ runner.os }}-${{ hashFiles('./examples/tutorial/basic-2/**/Cargo.toml') }}-${{ env.SOLANA_CLI_VERSION }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
name: basic-3 cache
|
||||
id: cache-basic-3
|
||||
with:
|
||||
path: ./examples/tutorial/basic-3/target
|
||||
key: cargo-${{ runner.os }}-${{ hashFiles('./examples/tutorial/basic-3/**/Cargo.toml') }}-${{ env.SOLANA_CLI_VERSION }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
name: basic-4 cache
|
||||
id: cache-basic-4
|
||||
with:
|
||||
|
@ -128,12 +133,14 @@ jobs:
|
|||
setup-client-example:
|
||||
needs: setup-anchor-cli
|
||||
name: Setup Client Example Test
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node:
|
||||
- path: tests/optional/
|
||||
name: optional.so
|
||||
- path: tests/events/
|
||||
name: events.so
|
||||
- path: examples/tutorial/basic-4/
|
||||
|
@ -143,18 +150,18 @@ jobs:
|
|||
- path: tests/composite/
|
||||
name: composite.so
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup/
|
||||
- uses: ./.github/actions/setup-solana/
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: anchor-binary
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +rwx ~/.cargo/bin/anchor
|
||||
|
||||
- run: cd ${{ matrix.node.path }} && anchor build --skip-lint
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.node.name }}
|
||||
path: ${{ matrix.node.path }}target/deploy/${{ matrix.node.name }}
|
||||
|
@ -163,36 +170,40 @@ jobs:
|
|||
test-client-example:
|
||||
needs: setup-client-example
|
||||
name: Client Example Test
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup/
|
||||
- uses: ./.github/actions/setup-ts/
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: anchor-binary
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/anchor
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: optional.so
|
||||
path: tests/optional/target/deploy/
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: events.so
|
||||
path: tests/events/target/deploy/
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: basic_4.so
|
||||
path: examples/tutorial/basic-4/target/deploy/
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: basic_2.so
|
||||
path: examples/tutorial/basic-2/target/deploy/
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: composite.so
|
||||
path: tests/composite/target/deploy/
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
name: Cache client/example target
|
||||
id: cache-test-target
|
||||
with:
|
||||
|
@ -205,15 +216,15 @@ jobs:
|
|||
test-bpf-upgradeable-state:
|
||||
needs: setup-anchor-cli
|
||||
name: Test tests/bpf-upgradeable-state
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup/
|
||||
- uses: ./.github/actions/setup-ts/
|
||||
- uses: ./.github/actions/setup-solana/
|
||||
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
name: Cache Cargo registry + index
|
||||
id: cache-anchor
|
||||
with:
|
||||
|
@ -223,15 +234,15 @@ jobs:
|
|||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
./target/
|
||||
key: cargo-${{ runner.os }}-anchor-${{ hashFiles('**/Cargo.lock') }}
|
||||
key: cargo-${{ runner.os }}-${{ env.CARGO_PROFILE }}-anchor-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: anchor-binary
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/anchor
|
||||
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
name: Cache tests/bpf-upgradeable-state target
|
||||
id: cache-test-target
|
||||
with:
|
||||
|
@ -248,69 +259,69 @@ jobs:
|
|||
- run: cd tests/bpf-upgradeable-state && npx tsc --noEmit
|
||||
- uses: ./.github/actions/git-diff/
|
||||
|
||||
# this test exists to make sure that anchor
|
||||
# checks rent correctly for legacy accounts
|
||||
# that don't have to be rent-exempt
|
||||
test-misc-non-rent-exempt:
|
||||
# the anchor cli is built with a different solana version
|
||||
# but that's fine since it's just the cli
|
||||
needs: setup-anchor-cli
|
||||
name: Test tests/misc/nonRentExempt
|
||||
runs-on: ubuntu-18.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/actions/setup/
|
||||
- uses: ./.github/actions/setup-ts/
|
||||
- uses: actions/cache@v2
|
||||
name: Cache Solana Tool Suite
|
||||
id: cache-solana
|
||||
with:
|
||||
path: |
|
||||
~/.cache/solana/
|
||||
~/.local/share/solana/
|
||||
key: solana-${{ runner.os }}-v0000-1.8.14
|
||||
# using an outdated validator but that
|
||||
# is ok as long as the test doesn't
|
||||
# include newer incompatible features
|
||||
- run: sh -c "$(curl -sSfL https://release.solana.com/v1.8.14/install)"
|
||||
shell: bash
|
||||
- run: echo "/home/runner/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH
|
||||
shell: bash
|
||||
- run: solana-keygen new --no-bip39-passphrase
|
||||
shell: bash
|
||||
- run: solana config set --url localhost
|
||||
shell: bash
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: anchor-binary
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/anchor
|
||||
# # this test exists to make sure that anchor
|
||||
# # checks rent correctly for legacy accounts
|
||||
# # that don't have to be rent-exempt
|
||||
# test-misc-non-rent-exempt:
|
||||
# # the anchor cli is built with a different solana version
|
||||
# # but that's fine since it's just the cli
|
||||
# needs: setup-anchor-cli
|
||||
# name: Test tests/misc/nonRentExempt
|
||||
# runs-on: ubuntu-latest
|
||||
# timeout-minutes: 30
|
||||
# steps:
|
||||
# - uses: actions/checkout@v3
|
||||
# - uses: ./.github/actions/setup/
|
||||
# - uses: ./.github/actions/setup-ts/
|
||||
# - uses: actions/cache@v3
|
||||
# name: Cache Solana Tool Suite
|
||||
# id: cache-solana
|
||||
# with:
|
||||
# path: |
|
||||
# ~/.cache/solana/
|
||||
# ~/.local/share/solana/
|
||||
# key: solana-${{ runner.os }}-v0000-1.8.14
|
||||
# # using an outdated validator but that
|
||||
# # is ok as long as the test doesn't
|
||||
# # include newer incompatible features
|
||||
# - run: sh -c "$(curl -sSfL https://release.solana.com/v1.8.14/install)"
|
||||
# shell: bash
|
||||
# - run: echo "/home/runner/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH
|
||||
# shell: bash
|
||||
# - run: solana-keygen new --no-bip39-passphrase
|
||||
# shell: bash
|
||||
# - run: solana config set --url localhost
|
||||
# shell: bash
|
||||
# - uses: actions/download-artifact@v3
|
||||
# with:
|
||||
# name: anchor-binary
|
||||
# path: ~/.cargo/bin/
|
||||
# - run: chmod +x ~/.cargo/bin/anchor
|
||||
|
||||
- uses: actions/cache@v2
|
||||
name: Cache tests/misc target
|
||||
id: cache-test-target
|
||||
with:
|
||||
path: tests/misc/target
|
||||
key: cargo-${{ runner.os }}-tests/misc-${{ env.ANCHOR_VERSION }}-1.8.14-${{ hashFiles('**/Cargo.lock') }}
|
||||
# - uses: actions/cache@v3
|
||||
# name: Cache tests/misc target
|
||||
# id: cache-test-target
|
||||
# with:
|
||||
# path: tests/misc/target
|
||||
# key: cargo-${{ runner.os }}-tests/misc-${{ env.ANCHOR_VERSION }}-1.8.14-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- run: cd tests/misc && yarn --frozen-lockfile
|
||||
- run: cd tests/misc
|
||||
- run: cd tests/misc && chmod +x ci.sh && ./ci.sh
|
||||
- run: cd tests/misc && anchor test --skip-lint
|
||||
# - run: cd tests/misc && yarn --frozen-lockfile
|
||||
# - run: cd tests/misc
|
||||
# - run: cd tests/misc && chmod +x ci.sh && ./ci.sh
|
||||
# - run: cd tests/misc && anchor test --skip-lint
|
||||
|
||||
test-anchor-init:
|
||||
needs: setup-anchor-cli
|
||||
name: Test Anchor Init
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup/
|
||||
- uses: ./.github/actions/setup-ts/
|
||||
- uses: ./.github/actions/setup-solana/
|
||||
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
name: Cache Cargo registry + index
|
||||
id: cache-anchor
|
||||
with:
|
||||
|
@ -320,21 +331,21 @@ jobs:
|
|||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
./target/
|
||||
key: cargo-${{ runner.os }}-anchor-${{ hashFiles('**/Cargo.lock') }}
|
||||
key: cargo-${{ runner.os }}-${{ env.CARGO_PROFILE }}-anchor-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: anchor-binary
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/anchor
|
||||
|
||||
- run: cd "$(mktemp -d)" && anchor init hello-anchor && cd hello-anchor && yarn link @project-serum/anchor && yarn && anchor test && yarn lint:fix
|
||||
- run: cd "$(mktemp -d)" && anchor init hello-anchor && cd hello-anchor && yarn link @coral-xyz/anchor && yarn && anchor test && yarn lint:fix
|
||||
- uses: ./.github/actions/git-diff/
|
||||
|
||||
test-programs:
|
||||
needs: setup-anchor-cli
|
||||
name: Test ${{ matrix.node.path }}
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
@ -350,11 +361,9 @@ jobs:
|
|||
path: spl/token-proxy
|
||||
- cmd: cd tests/multisig && anchor test --skip-lint
|
||||
path: tests/multisig
|
||||
- cmd: cd tests/interface && anchor test --skip-lint
|
||||
path: tests/interface
|
||||
- cmd: cd tests/lockup && anchor test --skip-lint
|
||||
path: tests/lockup
|
||||
- cmd: cd tests/swap/deps/serum-dex/dex && cargo build-bpf -- --locked && cd ../../../ && anchor test --skip-lint
|
||||
# - cmd: cd tests/lockup && anchor test --skip-lint
|
||||
# path: tests/lockup
|
||||
- cmd: cd tests/swap/deps/openbook-dex/dex && cargo build-bpf -- --locked && cd ../../../ && anchor test --skip-lint
|
||||
path: tests/swap
|
||||
- cmd: cd tests/escrow && anchor test --skip-lint && npx tsc --noEmit
|
||||
path: tests/escrow
|
||||
|
@ -380,8 +389,8 @@ jobs:
|
|||
path: tests/chat
|
||||
- cmd: cd tests/ido-pool && anchor test --skip-lint
|
||||
path: tests/ido-pool
|
||||
- cmd: cd tests/cfo && anchor run test-with-build && cd deps/stake && git checkout Cargo.lock && cd ../swap && git checkout Cargo.lock
|
||||
path: tests/cfo
|
||||
# - cmd: cd tests/cfo && anchor run test-with-build && cd deps/stake && git checkout Cargo.lock && cd ../swap && git checkout Cargo.lock
|
||||
# path: tests/cfo
|
||||
- cmd: cd tests/auction-house && yarn --frozen-lockfile && anchor test --skip-lint && git checkout Cargo.lock
|
||||
path: tests/auction-house
|
||||
- cmd: cd tests/floats && yarn --frozen-lockfile && anchor test --skip-lint && npx tsc --noEmit
|
||||
|
@ -396,17 +405,25 @@ jobs:
|
|||
path: tests/cpi-returns
|
||||
- cmd: cd tests/multiple-suites && anchor test --skip-lint && npx tsc --noEmit
|
||||
path: tests/multiple-suites
|
||||
- cmd: cd tests/multiple-suites-run-single && anchor test --skip-lint --run tests/should-run && npx tsc --noEmit
|
||||
path: tests/multiple-suites-run-single
|
||||
- cmd: cd tests/optional && anchor test --skip-lint && npx tsc --noEmit
|
||||
path: tests/optional
|
||||
- cmd: cd tests/pda-derivation && anchor test --skip-lint && npx tsc --noEmit
|
||||
path: tests/pda-derivation
|
||||
- cmd: cd tests/relations-derivation && anchor test --skip-lint && npx tsc --noEmit
|
||||
path: tests/relations-derivation
|
||||
- cmd: cd tests/anchor-cli-idl && ./test.sh
|
||||
path: tests/anchor-cli-idl
|
||||
- cmd: cd tests/anchor-cli-account && anchor test --skip-lint
|
||||
path: tests/anchor-cli-account
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup/
|
||||
- uses: ./.github/actions/setup-ts/
|
||||
- uses: ./.github/actions/setup-solana/
|
||||
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
name: Cache Cargo registry + index
|
||||
id: cache-anchor
|
||||
with:
|
||||
|
@ -416,15 +433,15 @@ jobs:
|
|||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
./target/
|
||||
key: cargo-${{ runner.os }}-anchor-${{ hashFiles('**/Cargo.lock') }}
|
||||
key: cargo-${{ runner.os }}-${{ env.CARGO_PROFILE }}-anchor-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: anchor-binary
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/anchor
|
||||
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
name: Cache ${{ matrix.node.path }} target
|
||||
id: cache-test-target
|
||||
with:
|
||||
|
|
|
@ -21,3 +21,5 @@ cli/npm-package/anchor
|
|||
cli/npm-package/*.tgz
|
||||
docker-target
|
||||
.rollup.cache/
|
||||
test-keypair.json
|
||||
tsconfig.tsbuildinfo
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
[submodule "examples/swap/deps/serum-dex"]
|
||||
path = tests/swap/deps/serum-dex
|
||||
url = https://github.com/project-serum/serum-dex
|
||||
[submodule "examples/cfo/deps/serum-dex"]
|
||||
path = tests/cfo/deps/serum-dex
|
||||
url = https://github.com/project-serum/serum-dex
|
||||
[submodule "examples/swap/deps/openbook-dex"]
|
||||
path = tests/swap/deps/openbook-dex
|
||||
url = https://github.com/openbook-dex/program
|
||||
[submodule "examples/cfo/deps/openbook-dex"]
|
||||
path = tests/cfo/deps/openbook-dex
|
||||
url = https://github.com/openbook-dex/program
|
||||
[submodule "examples/cfo/deps/swap"]
|
||||
path = tests/cfo/deps/swap
|
||||
url = https://github.com/project-serum/swap.git
|
||||
|
@ -12,9 +12,6 @@
|
|||
path = tests/cfo/deps/stake
|
||||
url = https://github.com/project-serum/stake.git
|
||||
branch = armani/cfo
|
||||
[submodule "examples/permissioned-markets/deps/serum-dex"]
|
||||
path = tests/permissioned-markets/deps/serum-dex
|
||||
url = https://github.com/project-serum/serum-dex
|
||||
[submodule "tests/auction-house"]
|
||||
path = tests/auction-house
|
||||
url = https://github.com/armaniferrante/auction-house
|
||||
|
|
680
CHANGELOG.md
680
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -15,7 +15,6 @@ members = [
|
|||
"spl",
|
||||
]
|
||||
exclude = [
|
||||
"tests/swap/deps/serum-dex",
|
||||
"tests/cfo/deps/serum-dex",
|
||||
"tests/permissioned-markets/deps/serum-dex",
|
||||
"tests/swap/deps/openbook-dex",
|
||||
"tests/cfo/deps/openbook-dex",
|
||||
]
|
||||
|
|
4
Makefile
4
Makefile
|
@ -23,12 +23,8 @@ publish:
|
|||
sleep 25
|
||||
cd lang/attribute/error/ && cargo publish && cd ../../../
|
||||
sleep 25
|
||||
cd lang/attribute/interface/ && cargo publish && cd ../../../
|
||||
sleep 25
|
||||
cd lang/attribute/program/ && cargo publish && cd ../../..
|
||||
sleep 25
|
||||
cd lang/attribute/state/ && cargo publish && cd ../../../
|
||||
sleep 25
|
||||
cd lang/attribute/event/ && cargo publish && cd ../../../
|
||||
sleep 25
|
||||
cd lang/ && cargo publish && cd ../
|
||||
|
|
18
README.md
18
README.md
|
@ -31,18 +31,18 @@ To jump straight to examples, go [here](https://github.com/coral-xyz/anchor/tree
|
|||
|
||||
## Packages
|
||||
|
||||
| Package | Description | Version | Docs |
|
||||
| :-------------------------- | :------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- |
|
||||
| `anchor-lang` | Rust primitives for writing programs on Solana | [![Crates.io](https://img.shields.io/crates/v/anchor-lang?color=blue)](https://crates.io/crates/anchor-lang) | [![Docs.rs](https://docs.rs/anchor-lang/badge.svg)](https://docs.rs/anchor-lang) |
|
||||
| `anchor-spl` | CPI clients for SPL programs on Solana | ![crates](https://img.shields.io/crates/v/anchor-spl?color=blue) | [![Docs.rs](https://docs.rs/anchor-spl/badge.svg)](https://docs.rs/anchor-spl) |
|
||||
| `anchor-client` | Rust client for Anchor programs | ![crates](https://img.shields.io/crates/v/anchor-client?color=blue) | [![Docs.rs](https://docs.rs/anchor-client/badge.svg)](https://docs.rs/anchor-client) |
|
||||
| `@project-serum/anchor` | TypeScript client for Anchor programs | [![npm](https://img.shields.io/npm/v/@project-serum/anchor.svg?color=blue)](https://www.npmjs.com/package/@project-serum/anchor) | [![Docs](https://img.shields.io/badge/docs-typedoc-blue)](https://coral-xyz.github.io/anchor/ts/index.html) |
|
||||
| `@project-serum/anchor-cli` | CLI to support building and managing an Anchor workspace | [![npm](https://img.shields.io/npm/v/@project-serum/anchor-cli.svg?color=blue)](https://www.npmjs.com/package/@project-serum/anchor-cli) | [![Docs](https://img.shields.io/badge/docs-typedoc-blue)](https://coral-xyz.github.io/anchor/cli/commands.html) |
|
||||
| Package | Description | Version | Docs |
|
||||
| :---------------------- | :------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- |
|
||||
| `anchor-lang` | Rust primitives for writing programs on Solana | [![Crates.io](https://img.shields.io/crates/v/anchor-lang?color=blue)](https://crates.io/crates/anchor-lang) | [![Docs.rs](https://docs.rs/anchor-lang/badge.svg)](https://docs.rs/anchor-lang) |
|
||||
| `anchor-spl` | CPI clients for SPL programs on Solana | ![crates](https://img.shields.io/crates/v/anchor-spl?color=blue) | [![Docs.rs](https://docs.rs/anchor-spl/badge.svg)](https://docs.rs/anchor-spl) |
|
||||
| `anchor-client` | Rust client for Anchor programs | ![crates](https://img.shields.io/crates/v/anchor-client?color=blue) | [![Docs.rs](https://docs.rs/anchor-client/badge.svg)](https://docs.rs/anchor-client) |
|
||||
| `@coral-xyz/anchor` | TypeScript client for Anchor programs | [![npm](https://img.shields.io/npm/v/@coral-xyz/anchor.svg?color=blue)](https://www.npmjs.com/package/@coral-xyz/anchor) | [![Docs](https://img.shields.io/badge/docs-typedoc-blue)](https://coral-xyz.github.io/anchor/ts/index.html) |
|
||||
| `@coral-xyz/anchor-cli` | CLI to support building and managing an Anchor workspace | [![npm](https://img.shields.io/npm/v/@coral-xyz/anchor-cli.svg?color=blue)](https://www.npmjs.com/package/@coral-xyz/anchor-cli) | [![Docs](https://img.shields.io/badge/docs-typedoc-blue)](https://coral-xyz.github.io/anchor/cli/commands.html) |
|
||||
|
||||
## Note
|
||||
|
||||
* **Anchor is in active development, so all APIs are subject to change.**
|
||||
* **This code is unaudited. Use at your own risk.**
|
||||
- **Anchor is in active development, so all APIs are subject to change.**
|
||||
- **This code is unaudited. Use at your own risk.**
|
||||
|
||||
## Examples
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "avm"
|
||||
version = "0.25.0"
|
||||
rust-version = "1.56"
|
||||
version = "0.26.0"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
|
@ -13,10 +13,10 @@ name = "anchor"
|
|||
path = "src/anchor/main.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.0.13", features = [ "derive" ]}
|
||||
clap = { version = "4.0.26", features = [ "derive" ]}
|
||||
cfg-if = "1.0.0"
|
||||
anyhow = "1.0.32"
|
||||
dirs = "1.0.5"
|
||||
dirs = "4.0.0"
|
||||
semver = "1.0.4"
|
||||
serde = { version = "1.0.136", features = [ "derive" ]}
|
||||
serde_json = "1.0.78"
|
||||
|
|
|
@ -88,7 +88,7 @@ pub fn install_version(version: &Version, force: bool) -> Result<()> {
|
|||
}
|
||||
|
||||
let exit = std::process::Command::new("cargo")
|
||||
.args(&[
|
||||
.args([
|
||||
"install",
|
||||
"--git",
|
||||
"https://github.com/coral-xyz/anchor",
|
||||
|
|
|
@ -15,12 +15,12 @@ pub struct Cli {
|
|||
pub enum Commands {
|
||||
#[clap(about = "Use a specific version of Anchor")]
|
||||
Use {
|
||||
#[clap(parse(try_from_str = parse_version))]
|
||||
#[clap(value_parser = parse_version)]
|
||||
version: Version,
|
||||
},
|
||||
#[clap(about = "Install a version of Anchor")]
|
||||
Install {
|
||||
#[clap(parse(try_from_str = parse_version))]
|
||||
#[clap(value_parser = parse_version)]
|
||||
version: Version,
|
||||
#[clap(long)]
|
||||
/// Flag to force installation even if the version
|
||||
|
@ -29,7 +29,7 @@ pub enum Commands {
|
|||
},
|
||||
#[clap(about = "Uninstall a version of Anchor")]
|
||||
Uninstall {
|
||||
#[clap(parse(try_from_str = parse_version))]
|
||||
#[clap(value_parser = parse_version)]
|
||||
version: Version,
|
||||
},
|
||||
#[clap(about = "List available versions of Anchor")]
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
[package]
|
||||
name = "anchor-cli"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
authors = ["armaniferrante <armaniferrante@gmail.com>"]
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/coral-xyz/anchor"
|
||||
description = "Anchor CLI"
|
||||
|
@ -17,31 +17,30 @@ dev = []
|
|||
default = []
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.0.13", features = ["derive"] }
|
||||
clap = { version = "4.0.26", features = ["derive"] }
|
||||
anyhow = "1.0.32"
|
||||
syn = { version = "1.0.60", features = ["full", "extra-traits"] }
|
||||
anchor-lang = { path = "../lang", version = "0.25.0" }
|
||||
anchor-client = { path = "../client", version = "0.25.0" }
|
||||
anchor-syn = { path = "../lang/syn", features = ["idl", "init-if-needed"], version = "0.25.0" }
|
||||
anchor-lang = { path = "../lang", version = "0.26.0" }
|
||||
anchor-client = { path = "../client", version = "0.26.0" }
|
||||
anchor-syn = { path = "../lang/syn", features = ["idl", "init-if-needed"], version = "0.26.0" }
|
||||
serde_json = "1.0"
|
||||
shellexpand = "2.1.0"
|
||||
toml = "0.5.8"
|
||||
semver = "1.0.4"
|
||||
serde = { version = "1.0.122", features = ["derive"] }
|
||||
solana-sdk = "~1.10.29"
|
||||
solana-program = "~1.10.29"
|
||||
solana-client = "~1.10.29"
|
||||
solana-cli-config = "~1.10.29"
|
||||
solana-faucet = "~1.10.29"
|
||||
dirs = "3.0"
|
||||
heck = "0.3.1"
|
||||
solana-sdk = "1.13.5"
|
||||
solana-program = "1.13.5"
|
||||
solana-client = "1.13.5"
|
||||
solana-cli-config = "1.13.5"
|
||||
solana-faucet = "1.13.5"
|
||||
dirs = "4.0"
|
||||
heck = "0.4.0"
|
||||
flate2 = "1.0.19"
|
||||
rand = "0.7.3"
|
||||
tar = "0.4.35"
|
||||
reqwest = { version = "0.11.4", features = ["multipart", "blocking"] }
|
||||
tokio = "1.0"
|
||||
pathdiff = "0.2.0"
|
||||
cargo_toml = "0.9.2"
|
||||
cargo_toml = "0.13.0"
|
||||
walkdir = "2.3.2"
|
||||
chrono = "0.4.19"
|
||||
portpicker = "0.1.1"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@project-serum/anchor-cli",
|
||||
"version": "0.25.0",
|
||||
"name": "@coral-xyz/anchor-cli",
|
||||
"version": "0.26.0",
|
||||
"description": "Anchor CLI tool",
|
||||
"homepage": "https://github.com/coral-xyz/anchor#readme",
|
||||
"bugs": {
|
||||
|
|
|
@ -2,21 +2,24 @@ use crate::is_hidden;
|
|||
use anchor_client::Cluster;
|
||||
use anchor_syn::idl::Idl;
|
||||
use anyhow::{anyhow, Context, Error, Result};
|
||||
use clap::{ArgEnum, Parser};
|
||||
use heck::SnakeCase;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use clap::{Parser, ValueEnum};
|
||||
use heck::ToSnakeCase;
|
||||
use reqwest::Url;
|
||||
use serde::de::{self, MapAccess, Visitor};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use solana_cli_config::{Config as SolanaConfig, CONFIG_FILE};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::convert::TryFrom;
|
||||
use std::fs::{self, File};
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::{fmt, io};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
pub trait Merge: Sized {
|
||||
|
@ -94,7 +97,7 @@ impl Manifest {
|
|||
|
||||
pub fn version(&self) -> String {
|
||||
match &self.package {
|
||||
Some(package) => package.version.to_string(),
|
||||
Some(package) => package.version().to_string(),
|
||||
_ => "0.0.0".to_string(),
|
||||
}
|
||||
}
|
||||
|
@ -171,20 +174,18 @@ impl WithPath<Config> {
|
|||
.collect())
|
||||
}
|
||||
|
||||
// TODO: this should read idl dir instead of parsing source.
|
||||
pub fn read_all_programs(&self) -> Result<Vec<Program>> {
|
||||
let mut r = vec![];
|
||||
for path in self.get_program_list()? {
|
||||
let cargo = Manifest::from_path(&path.join("Cargo.toml"))?;
|
||||
let lib_name = cargo.lib_name()?;
|
||||
let version = cargo.version();
|
||||
let idl = anchor_syn::idl::file::parse(
|
||||
path.join("src/lib.rs"),
|
||||
version,
|
||||
self.features.seeds,
|
||||
false,
|
||||
false,
|
||||
)?;
|
||||
|
||||
let idl_filepath = format!("target/idl/{}.json", lib_name);
|
||||
let idl = fs::read(idl_filepath)
|
||||
.ok()
|
||||
.map(|bytes| serde_json::from_reader(&*bytes))
|
||||
.transpose()?;
|
||||
|
||||
r.push(Program {
|
||||
lib_name,
|
||||
path,
|
||||
|
@ -323,7 +324,7 @@ pub struct WorkspaceConfig {
|
|||
pub types: String,
|
||||
}
|
||||
|
||||
#[derive(ArgEnum, Parser, Clone, PartialEq, Debug)]
|
||||
#[derive(ValueEnum, Parser, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum BootstrapMode {
|
||||
None,
|
||||
Debian,
|
||||
|
@ -338,8 +339,12 @@ pub struct BuildConfig {
|
|||
}
|
||||
|
||||
impl Config {
|
||||
pub fn add_test_config(&mut self, root: impl AsRef<Path>) -> Result<()> {
|
||||
self.test_config = TestConfig::discover(root)?;
|
||||
pub fn add_test_config(
|
||||
&mut self,
|
||||
root: impl AsRef<Path>,
|
||||
test_paths: Vec<PathBuf>,
|
||||
) -> Result<()> {
|
||||
self.test_config = TestConfig::discover(root, test_paths)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -420,10 +425,58 @@ struct _Config {
|
|||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct Provider {
|
||||
cluster: String,
|
||||
#[serde(deserialize_with = "des_cluster")]
|
||||
cluster: Cluster,
|
||||
wallet: String,
|
||||
}
|
||||
|
||||
fn des_cluster<'de, D>(deserializer: D) -> Result<Cluster, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct StringOrCustomCluster(PhantomData<fn() -> Cluster>);
|
||||
|
||||
impl<'de> Visitor<'de> for StringOrCustomCluster {
|
||||
type Value = Cluster;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("string or map")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Cluster, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
value.parse().map_err(de::Error::custom)
|
||||
}
|
||||
|
||||
fn visit_map<M>(self, mut map: M) -> Result<Cluster, M::Error>
|
||||
where
|
||||
M: MapAccess<'de>,
|
||||
{
|
||||
// Gets keys
|
||||
if let (Some((http_key, http_value)), Some((ws_key, ws_value))) = (
|
||||
map.next_entry::<String, String>()?,
|
||||
map.next_entry::<String, String>()?,
|
||||
) {
|
||||
// Checks keys
|
||||
if http_key != "http" || ws_key != "ws" {
|
||||
return Err(de::Error::custom("Invalid key"));
|
||||
}
|
||||
|
||||
// Checks urls
|
||||
Url::parse(&http_value).map_err(de::Error::custom)?;
|
||||
Url::parse(&ws_value).map_err(de::Error::custom)?;
|
||||
|
||||
Ok(Cluster::Custom(http_value, ws_value))
|
||||
} else {
|
||||
Err(de::Error::custom("Invalid entry"))
|
||||
}
|
||||
}
|
||||
}
|
||||
deserializer.deserialize_any(StringOrCustomCluster(PhantomData))
|
||||
}
|
||||
|
||||
impl ToString for Config {
|
||||
fn to_string(&self) -> String {
|
||||
let programs = {
|
||||
|
@ -440,7 +493,7 @@ impl ToString for Config {
|
|||
features: Some(self.features.clone()),
|
||||
registry: Some(self.registry.clone()),
|
||||
provider: Provider {
|
||||
cluster: format!("{}", self.provider.cluster),
|
||||
cluster: self.provider.cluster.clone(),
|
||||
wallet: self.provider.wallet.to_string(),
|
||||
},
|
||||
test: self.test_validator.clone().map(Into::into),
|
||||
|
@ -469,7 +522,7 @@ impl FromStr for Config {
|
|||
features: cfg.features.unwrap_or_default(),
|
||||
registry: cfg.registry.unwrap_or_default(),
|
||||
provider: ProviderConfig {
|
||||
cluster: cfg.provider.cluster.parse()?,
|
||||
cluster: cfg.provider.cluster,
|
||||
wallet: shellexpand::tilde(&cfg.provider.wallet).parse()?,
|
||||
},
|
||||
scripts: cfg.scripts.unwrap_or_default(),
|
||||
|
@ -537,6 +590,7 @@ fn deser_programs(
|
|||
path: None,
|
||||
idl: None,
|
||||
},
|
||||
|
||||
serde_json::Value::Object(_) => {
|
||||
serde_json::from_value(program_id.clone())
|
||||
.map_err(|_| anyhow!("Unable to read toml"))?
|
||||
|
@ -610,14 +664,17 @@ impl Deref for TestConfig {
|
|||
}
|
||||
|
||||
impl TestConfig {
|
||||
pub fn discover(root: impl AsRef<Path>) -> Result<Option<Self>> {
|
||||
pub fn discover(root: impl AsRef<Path>, test_paths: Vec<PathBuf>) -> Result<Option<Self>> {
|
||||
let walker = WalkDir::new(root).into_iter();
|
||||
let mut test_suite_configs = HashMap::new();
|
||||
for entry in walker.filter_entry(|e| !is_hidden(e)) {
|
||||
let entry = entry?;
|
||||
if entry.file_name() == "Test.toml" {
|
||||
let test_toml = TestToml::from_path(entry.path())?;
|
||||
test_suite_configs.insert(entry.path().into(), test_toml);
|
||||
let entry_path = entry.path();
|
||||
let test_toml = TestToml::from_path(entry_path)?;
|
||||
if test_paths.is_empty() || test_paths.iter().any(|p| entry_path.starts_with(p)) {
|
||||
test_suite_configs.insert(entry.path().into(), test_toml);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -833,6 +890,9 @@ pub struct _Validator {
|
|||
// Give the faucet address this much SOL in genesis. [default: 1000000]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub faucet_sol: Option<String>,
|
||||
// Geyser plugin config location
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub geyser_plugin_config: Option<String>,
|
||||
// Gossip DNS name or IP address for the validator to advertise in gossip. [default: 127.0.0.1]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub gossip_host: Option<String>,
|
||||
|
@ -854,6 +914,9 @@ pub struct _Validator {
|
|||
// Override the number of slots in an epoch.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub slots_per_epoch: Option<String>,
|
||||
// The number of ticks in a slot
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ticks_per_slot: Option<u16>,
|
||||
// Warp the ledger to WARP_SLOT after starting the validator.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub warp_slot: Option<String>,
|
||||
|
@ -873,6 +936,8 @@ pub struct Validator {
|
|||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub faucet_sol: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub geyser_plugin_config: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub gossip_host: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub gossip_port: Option<u16>,
|
||||
|
@ -885,6 +950,8 @@ pub struct Validator {
|
|||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub slots_per_epoch: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ticks_per_slot: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub warp_slot: Option<String>,
|
||||
}
|
||||
|
||||
|
@ -899,6 +966,7 @@ impl From<_Validator> for Validator {
|
|||
dynamic_port_range: _validator.dynamic_port_range,
|
||||
faucet_port: _validator.faucet_port,
|
||||
faucet_sol: _validator.faucet_sol,
|
||||
geyser_plugin_config: _validator.geyser_plugin_config,
|
||||
gossip_host: _validator.gossip_host,
|
||||
gossip_port: _validator.gossip_port,
|
||||
url: _validator.url,
|
||||
|
@ -910,6 +978,7 @@ impl From<_Validator> for Validator {
|
|||
.rpc_port
|
||||
.unwrap_or(solana_sdk::rpc_port::DEFAULT_RPC_PORT),
|
||||
slots_per_epoch: _validator.slots_per_epoch,
|
||||
ticks_per_slot: _validator.ticks_per_slot,
|
||||
warp_slot: _validator.warp_slot,
|
||||
}
|
||||
}
|
||||
|
@ -924,6 +993,7 @@ impl From<Validator> for _Validator {
|
|||
dynamic_port_range: validator.dynamic_port_range,
|
||||
faucet_port: validator.faucet_port,
|
||||
faucet_sol: validator.faucet_sol,
|
||||
geyser_plugin_config: validator.geyser_plugin_config,
|
||||
gossip_host: validator.gossip_host,
|
||||
gossip_port: validator.gossip_port,
|
||||
url: validator.url,
|
||||
|
@ -931,6 +1001,7 @@ impl From<Validator> for _Validator {
|
|||
limit_ledger_size: validator.limit_ledger_size,
|
||||
rpc_port: Some(validator.rpc_port),
|
||||
slots_per_epoch: validator.slots_per_epoch,
|
||||
ticks_per_slot: validator.ticks_per_slot,
|
||||
warp_slot: validator.warp_slot,
|
||||
}
|
||||
}
|
||||
|
@ -987,6 +1058,9 @@ impl Merge for _Validator {
|
|||
.or_else(|| self.dynamic_port_range.take()),
|
||||
faucet_port: other.faucet_port.or_else(|| self.faucet_port.take()),
|
||||
faucet_sol: other.faucet_sol.or_else(|| self.faucet_sol.take()),
|
||||
geyser_plugin_config: other
|
||||
.geyser_plugin_config
|
||||
.or_else(|| self.geyser_plugin_config.take()),
|
||||
gossip_host: other.gossip_host.or_else(|| self.gossip_host.take()),
|
||||
gossip_port: other.gossip_port.or_else(|| self.gossip_port.take()),
|
||||
url: other.url.or_else(|| self.url.take()),
|
||||
|
@ -998,6 +1072,7 @@ impl Merge for _Validator {
|
|||
slots_per_epoch: other
|
||||
.slots_per_epoch
|
||||
.or_else(|| self.slots_per_epoch.take()),
|
||||
ticks_per_slot: other.ticks_per_slot.or_else(|| self.ticks_per_slot.take()),
|
||||
warp_slot: other.warp_slot.or_else(|| self.warp_slot.take()),
|
||||
};
|
||||
}
|
||||
|
@ -1037,7 +1112,7 @@ impl Program {
|
|||
path,
|
||||
));
|
||||
}
|
||||
let program_kp = Keypair::generate(&mut rand::rngs::OsRng);
|
||||
let program_kp = Keypair::new();
|
||||
let mut file = File::create(&path)
|
||||
.with_context(|| format!("Error creating file with path: {}", path.display()))?;
|
||||
file.write_all(format!("{:?}", &program_kp.to_bytes()).as_bytes())?;
|
||||
|
@ -1129,6 +1204,18 @@ mod tests {
|
|||
wallet = \"id.json\"
|
||||
";
|
||||
|
||||
const CUSTOM_CONFIG: &str = "
|
||||
[provider]
|
||||
cluster = { http = \"http://my-url.com\", ws = \"ws://my-url.com\" }
|
||||
wallet = \"id.json\"
|
||||
";
|
||||
|
||||
#[test]
|
||||
fn parse_custom_cluster() {
|
||||
let config = Config::from_str(CUSTOM_CONFIG).unwrap();
|
||||
assert!(!config.features.skip_lint);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_skip_lint_no_section() {
|
||||
let config = Config::from_str(BASE_CONFIG).unwrap();
|
||||
|
|
569
cli/src/lib.rs
569
cli/src/lib.rs
File diff suppressed because it is too large
Load Diff
|
@ -2,7 +2,7 @@ use crate::config::ProgramWorkspace;
|
|||
use crate::VERSION;
|
||||
use anchor_syn::idl::Idl;
|
||||
use anyhow::Result;
|
||||
use heck::{CamelCase, MixedCase, SnakeCase};
|
||||
use heck::{ToLowerCamelCase, ToSnakeCase, ToUpperCamelCase};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use std::fmt::Write;
|
||||
|
||||
|
@ -41,7 +41,7 @@ token = "{}"
|
|||
pub fn idl_ts(idl: &Idl) -> Result<String> {
|
||||
let mut idl = idl.clone();
|
||||
for acc in idl.accounts.iter_mut() {
|
||||
acc.name = acc.name.to_mixed_case();
|
||||
acc.name = acc.name.to_lower_camel_case();
|
||||
}
|
||||
let idl_json = serde_json::to_string_pretty(&idl)?;
|
||||
Ok(format!(
|
||||
|
@ -49,9 +49,9 @@ pub fn idl_ts(idl: &Idl) -> Result<String> {
|
|||
|
||||
export const IDL: {} = {};
|
||||
"#,
|
||||
idl.name.to_camel_case(),
|
||||
idl.name.to_upper_camel_case(),
|
||||
idl_json,
|
||||
idl.name.to_camel_case(),
|
||||
idl.name.to_upper_camel_case(),
|
||||
idl_json
|
||||
))
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ anchor-lang = "{2}"
|
|||
pub fn deploy_js_script_host(cluster_url: &str, script_path: &str) -> String {
|
||||
format!(
|
||||
r#"
|
||||
const anchor = require('@project-serum/anchor');
|
||||
const anchor = require('@coral-xyz/anchor');
|
||||
|
||||
// Deploy script defined by the user.
|
||||
const userScript = require("{0}");
|
||||
|
@ -114,7 +114,7 @@ main();
|
|||
|
||||
pub fn deploy_ts_script_host(cluster_url: &str, script_path: &str) -> String {
|
||||
format!(
|
||||
r#"import * as anchor from '@project-serum/anchor';
|
||||
r#"import * as anchor from '@coral-xyz/anchor';
|
||||
|
||||
// Deploy script defined by the user.
|
||||
const userScript = require("{0}");
|
||||
|
@ -144,7 +144,7 @@ pub fn deploy_script() -> &'static str {
|
|||
// single deploy script that's invoked from the CLI, injecting a provider
|
||||
// configured from the workspace's Anchor.toml.
|
||||
|
||||
const anchor = require("@project-serum/anchor");
|
||||
const anchor = require("@coral-xyz/anchor");
|
||||
|
||||
module.exports = async function (provider) {
|
||||
// Configure client to use the provider.
|
||||
|
@ -160,7 +160,7 @@ pub fn ts_deploy_script() -> &'static str {
|
|||
// single deploy script that's invoked from the CLI, injecting a provider
|
||||
// configured from the workspace's Anchor.toml.
|
||||
|
||||
const anchor = require("@project-serum/anchor");
|
||||
const anchor = require("@coral-xyz/anchor");
|
||||
|
||||
module.exports = async function (provider) {
|
||||
// Configure client to use the provider.
|
||||
|
@ -202,7 +202,7 @@ pub struct Initialize {{}}
|
|||
|
||||
pub fn mocha(name: &str) -> String {
|
||||
format!(
|
||||
r#"const anchor = require("@project-serum/anchor");
|
||||
r#"const anchor = require("@coral-xyz/anchor");
|
||||
|
||||
describe("{}", () => {{
|
||||
// Configure the client to use the local cluster.
|
||||
|
@ -217,19 +217,59 @@ describe("{}", () => {{
|
|||
}});
|
||||
"#,
|
||||
name,
|
||||
name.to_camel_case(),
|
||||
name.to_upper_camel_case(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn package_json() -> String {
|
||||
pub fn jest(name: &str) -> String {
|
||||
format!(
|
||||
r#"{{
|
||||
r#"const anchor = require("@coral-xyz/anchor");
|
||||
|
||||
describe("{}", () => {{
|
||||
// Configure the client to use the local cluster.
|
||||
anchor.setProvider(anchor.AnchorProvider.env());
|
||||
|
||||
it("Is initialized!", async () => {{
|
||||
// Add your test here.
|
||||
const program = anchor.workspace.{};
|
||||
const tx = await program.methods.initialize().rpc();
|
||||
console.log("Your transaction signature", tx);
|
||||
}});
|
||||
}});
|
||||
"#,
|
||||
name,
|
||||
name.to_upper_camel_case(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn package_json(jest: bool) -> String {
|
||||
if jest {
|
||||
format!(
|
||||
r#"{{
|
||||
"scripts": {{
|
||||
"lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
|
||||
"lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
|
||||
}},
|
||||
"dependencies": {{
|
||||
"@coral-xyz/anchor": "^{0}"
|
||||
}},
|
||||
"devDependencies": {{
|
||||
"jest": "^29.0.3",
|
||||
"prettier": "^2.6.2"
|
||||
}}
|
||||
}}
|
||||
"#,
|
||||
VERSION
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
r#"{{
|
||||
"scripts": {{
|
||||
"lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
|
||||
"lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
|
||||
}},
|
||||
"dependencies": {{
|
||||
"@project-serum/anchor": "^{0}"
|
||||
"@coral-xyz/anchor": "^{0}"
|
||||
}},
|
||||
"devDependencies": {{
|
||||
"chai": "^4.3.4",
|
||||
|
@ -238,19 +278,43 @@ pub fn package_json() -> String {
|
|||
}}
|
||||
}}
|
||||
"#,
|
||||
VERSION
|
||||
)
|
||||
VERSION
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ts_package_json() -> String {
|
||||
format!(
|
||||
r#"{{
|
||||
pub fn ts_package_json(jest: bool) -> String {
|
||||
if jest {
|
||||
format!(
|
||||
r#"{{
|
||||
"scripts": {{
|
||||
"lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
|
||||
"lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
|
||||
}},
|
||||
"dependencies": {{
|
||||
"@coral-xyz/anchor": "^{0}"
|
||||
}},
|
||||
"devDependencies": {{
|
||||
"@types/bn.js": "^5.1.0",
|
||||
"@types/jest": "^29.0.3",
|
||||
"jest": "^29.0.3",
|
||||
"prettier": "^2.6.2",
|
||||
"ts-jest": "^29.0.2",
|
||||
"typescript": "^4.3.5"
|
||||
}}
|
||||
}}
|
||||
"#,
|
||||
VERSION
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
r#"{{
|
||||
"scripts": {{
|
||||
"lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
|
||||
"lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
|
||||
}},
|
||||
"dependencies": {{
|
||||
"@project-serum/anchor": "^{0}"
|
||||
"@coral-xyz/anchor": "^{0}"
|
||||
}},
|
||||
"devDependencies": {{
|
||||
"chai": "^4.3.4",
|
||||
|
@ -264,14 +328,15 @@ pub fn ts_package_json() -> String {
|
|||
}}
|
||||
}}
|
||||
"#,
|
||||
VERSION
|
||||
)
|
||||
VERSION
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ts_mocha(name: &str) -> String {
|
||||
format!(
|
||||
r#"import * as anchor from "@project-serum/anchor";
|
||||
import {{ Program }} from "@project-serum/anchor";
|
||||
r#"import * as anchor from "@coral-xyz/anchor";
|
||||
import {{ Program }} from "@coral-xyz/anchor";
|
||||
import {{ {} }} from "../target/types/{}";
|
||||
|
||||
describe("{}", () => {{
|
||||
|
@ -287,26 +352,67 @@ describe("{}", () => {{
|
|||
}});
|
||||
}});
|
||||
"#,
|
||||
name.to_camel_case(),
|
||||
name.to_upper_camel_case(),
|
||||
name.to_snake_case(),
|
||||
name,
|
||||
name.to_camel_case(),
|
||||
name.to_camel_case(),
|
||||
name.to_upper_camel_case(),
|
||||
name.to_upper_camel_case(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn ts_config() -> &'static str {
|
||||
r#"{
|
||||
"compilerOptions": {
|
||||
"types": ["mocha", "chai"],
|
||||
"typeRoots": ["./node_modules/@types"],
|
||||
"lib": ["es2015"],
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"esModuleInterop": true
|
||||
}
|
||||
pub fn ts_jest(name: &str) -> String {
|
||||
format!(
|
||||
r#"import * as anchor from "@coral-xyz/anchor";
|
||||
import {{ Program }} from "@coral-xyz/anchor";
|
||||
import {{ {} }} from "../target/types/{}";
|
||||
|
||||
describe("{}", () => {{
|
||||
// Configure the client to use the local cluster.
|
||||
anchor.setProvider(anchor.AnchorProvider.env());
|
||||
|
||||
const program = anchor.workspace.{} as Program<{}>;
|
||||
|
||||
it("Is initialized!", async () => {{
|
||||
// Add your test here.
|
||||
const tx = await program.methods.initialize().rpc();
|
||||
console.log("Your transaction signature", tx);
|
||||
}});
|
||||
}});
|
||||
"#,
|
||||
name.to_upper_camel_case(),
|
||||
name.to_snake_case(),
|
||||
name,
|
||||
name.to_upper_camel_case(),
|
||||
name.to_upper_camel_case(),
|
||||
)
|
||||
}
|
||||
"#
|
||||
|
||||
pub fn ts_config(jest: bool) -> &'static str {
|
||||
if jest {
|
||||
r#"{
|
||||
"compilerOptions": {
|
||||
"types": ["jest"],
|
||||
"typeRoots": ["./node_modules/@types"],
|
||||
"lib": ["es2015"],
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
||||
"#
|
||||
} else {
|
||||
r#"{
|
||||
"compilerOptions": {
|
||||
"types": ["mocha", "chai"],
|
||||
"typeRoots": ["./node_modules/@types"],
|
||||
"lib": ["es2015"],
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
||||
"#
|
||||
}
|
||||
}
|
||||
|
||||
pub fn git_ignore() -> &'static str {
|
||||
|
@ -339,7 +445,7 @@ pub fn node_shell(
|
|||
) -> Result<String> {
|
||||
let mut eval_string = format!(
|
||||
r#"
|
||||
const anchor = require('@project-serum/anchor');
|
||||
const anchor = require('@coral-xyz/anchor');
|
||||
const web3 = anchor.web3;
|
||||
const PublicKey = anchor.web3.PublicKey;
|
||||
const Keypair = anchor.web3.Keypair;
|
||||
|
@ -374,7 +480,7 @@ anchor.setProvider(provider);
|
|||
r#"
|
||||
anchor.workspace.{} = new anchor.Program({}, new PublicKey("{}"), provider);
|
||||
"#,
|
||||
program.name.to_camel_case(),
|
||||
program.name.to_upper_camel_case(),
|
||||
serde_json::to_string(&program.idl)?,
|
||||
program.program_id
|
||||
)?;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
[package]
|
||||
name = "anchor-client"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
description = "Rust client for Anchor programs"
|
||||
|
@ -11,12 +11,12 @@ description = "Rust client for Anchor programs"
|
|||
debug = []
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = { path = "../lang", version = "0.25.0" }
|
||||
anchor-lang = { path = "../lang", version = "0.26.0" }
|
||||
anyhow = "1.0.32"
|
||||
regex = "1.4.5"
|
||||
serde = { version = "1.0.122", features = ["derive"] }
|
||||
solana-client = "~1.10.29"
|
||||
solana-sdk = "~1.10.29"
|
||||
solana-account-decoder = "~1.10.29"
|
||||
solana-client = "1.14.7"
|
||||
solana-sdk = "1.13.5"
|
||||
solana-account-decoder = "1.13.5"
|
||||
thiserror = "1.0.20"
|
||||
url = "2.2.2"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
name = "example"
|
||||
version = "0.1.0"
|
||||
authors = ["Armani Ferrante <armaniferrante@gmail.com>"]
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
|
||||
[workspace]
|
||||
|
@ -12,9 +12,9 @@ anchor-client = { path = "../", features = ["debug"] }
|
|||
basic-2 = { path = "../../examples/tutorial/basic-2/programs/basic-2", features = ["no-entrypoint"] }
|
||||
basic-4 = { path = "../../examples/tutorial/basic-4/programs/basic-4", features = ["no-entrypoint"] }
|
||||
composite = { path = "../../tests/composite/programs/composite", features = ["no-entrypoint"] }
|
||||
optional = { path = "../../tests/optional/programs/optional", features = ["no-entrypoint"] }
|
||||
events = { path = "../../tests/events/programs/events", features = ["no-entrypoint"] }
|
||||
shellexpand = "2.1.0"
|
||||
anyhow = "1.0.32"
|
||||
rand = "0.7.3"
|
||||
clap = { version = "3.0.0-rc.0", features = ["derive"] }
|
||||
solana-sdk = "~1.10.29"
|
||||
clap = { version = "4.0.26", features = ["derive"] }
|
||||
solana-sdk = "1.13.5"
|
||||
|
|
|
@ -26,6 +26,7 @@ main() {
|
|||
local basic_2_pid="Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
|
||||
local basic_4_pid="CwrqeMj2U8tFr1Rhkgwc84tpAsqbt9pTt2a4taoTADPr"
|
||||
local events_pid="2dhGsWUzy5YKUsjZdLHLmkNpUDAXkNa9MYWsPc4Ziqzy"
|
||||
local optional_pid="FNqz6pqLAwvMSds2FYjR4nKV3moVpPNtvkfGFrqLKrgG"
|
||||
|
||||
#
|
||||
# Bootup validator.
|
||||
|
@ -35,13 +36,14 @@ main() {
|
|||
--bpf-program $basic_2_pid ../../examples/tutorial/basic-2/target/deploy/basic_2.so \
|
||||
--bpf-program $basic_4_pid ../../examples/tutorial/basic-4/target/deploy/basic_4.so \
|
||||
--bpf-program $events_pid ../../tests/events/target/deploy/events.so \
|
||||
--bpf-program $optional_pid ../../tests/optional/target/deploy/optional.so \
|
||||
> test-validator.log &
|
||||
sleep 5
|
||||
|
||||
#
|
||||
# Run Test.
|
||||
#
|
||||
cargo run -- --composite-pid $composite_pid --basic-2-pid $basic_2_pid --basic-4-pid $basic_4_pid --events-pid $events_pid
|
||||
cargo run -- --composite-pid $composite_pid --basic-2-pid $basic_2_pid --basic-4-pid $basic_4_pid --events-pid $events_pid --optional-pid $optional_pid
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
|
|
|
@ -12,16 +12,18 @@ use basic_2::instruction as basic_2_instruction;
|
|||
use basic_2::Counter;
|
||||
use events::instruction as events_instruction;
|
||||
use events::MyEvent;
|
||||
use optional::accounts::Initialize as OptionalInitialize;
|
||||
use optional::instruction as optional_instruction;
|
||||
// The `accounts` and `instructions` modules are generated by the framework.
|
||||
use basic_4::accounts as basic_4_accounts;
|
||||
use basic_4::basic_4::Counter as CounterState;
|
||||
use basic_4::instruction as basic_4_instruction;
|
||||
use basic_4::Counter as CounterAccount;
|
||||
use clap::Parser;
|
||||
// The `accounts` and `instructions` modules are generated by the framework.
|
||||
use composite::accounts::{Bar, CompositeUpdate, Foo, Initialize};
|
||||
use composite::instruction as composite_instruction;
|
||||
use composite::{DummyA, DummyB};
|
||||
use rand::rngs::OsRng;
|
||||
use optional::account::{DataAccount, DataPda};
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
|
@ -35,6 +37,8 @@ pub struct Opts {
|
|||
basic_4_pid: Pubkey,
|
||||
#[clap(long)]
|
||||
events_pid: Pubkey,
|
||||
#[clap(long)]
|
||||
optional_pid: Pubkey,
|
||||
}
|
||||
|
||||
// This example assumes a local validator is running with the programs
|
||||
|
@ -59,6 +63,7 @@ fn main() -> Result<()> {
|
|||
basic_2(&client, opts.basic_2_pid)?;
|
||||
basic_4(&client, opts.basic_4_pid)?;
|
||||
events(&client, opts.events_pid)?;
|
||||
optional(&client, opts.optional_pid)?;
|
||||
|
||||
// Success.
|
||||
Ok(())
|
||||
|
@ -72,8 +77,8 @@ fn composite(client: &Client, pid: Pubkey) -> Result<()> {
|
|||
let program = client.program(pid);
|
||||
|
||||
// `Initialize` parameters.
|
||||
let dummy_a = Keypair::generate(&mut OsRng);
|
||||
let dummy_b = Keypair::generate(&mut OsRng);
|
||||
let dummy_a = Keypair::new();
|
||||
let dummy_b = Keypair::new();
|
||||
|
||||
// Build and send a transaction.
|
||||
program
|
||||
|
@ -142,7 +147,7 @@ fn basic_2(client: &Client, pid: Pubkey) -> Result<()> {
|
|||
let program = client.program(pid);
|
||||
|
||||
// `Create` parameters.
|
||||
let counter = Keypair::generate(&mut OsRng);
|
||||
let counter = Keypair::new();
|
||||
let authority = program.payer();
|
||||
|
||||
// Build and send a transaction.
|
||||
|
@ -202,24 +207,28 @@ fn events(client: &Client, pid: Pubkey) -> Result<()> {
|
|||
pub fn basic_4(client: &Client, pid: Pubkey) -> Result<()> {
|
||||
let program = client.program(pid);
|
||||
let authority = program.payer();
|
||||
let (counter, _) = Pubkey::find_program_address(&[b"counter"], &pid);
|
||||
|
||||
// Invoke the state's `new` constructor.
|
||||
program
|
||||
.state_request()
|
||||
.accounts(basic_4_accounts::Auth { authority })
|
||||
.new(basic_4_instruction::state::New)
|
||||
.request()
|
||||
.accounts(basic_4_accounts::Initialize {
|
||||
counter,
|
||||
authority,
|
||||
system_program: system_program::ID,
|
||||
})
|
||||
.args(basic_4_instruction::Initialize {})
|
||||
.send()?;
|
||||
let counter_account: CounterState = program.state()?;
|
||||
let counter_account: CounterAccount = program.account(counter)?;
|
||||
assert_eq!(counter_account.authority, authority);
|
||||
assert_eq!(counter_account.count, 0);
|
||||
|
||||
// Call a state method.
|
||||
program
|
||||
.state_request()
|
||||
.accounts(basic_4_accounts::Auth { authority })
|
||||
.args(basic_4_instruction::state::Increment)
|
||||
.request()
|
||||
.accounts(basic_4_accounts::Increment { counter, authority })
|
||||
.args(basic_4_instruction::Increment {})
|
||||
.send()?;
|
||||
let counter_account: CounterState = program.state()?;
|
||||
|
||||
let counter_account: CounterAccount = program.account(counter)?;
|
||||
assert_eq!(counter_account.authority, authority);
|
||||
assert_eq!(counter_account.count, 1);
|
||||
|
||||
|
@ -227,3 +236,61 @@ pub fn basic_4(client: &Client, pid: Pubkey) -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Runs a client for tests/optional.
|
||||
//
|
||||
// Make sure to run a localnet with the program deploy to run this example.
|
||||
fn optional(client: &Client, pid: Pubkey) -> Result<()> {
|
||||
// Program client.
|
||||
let program = client.program(pid);
|
||||
|
||||
// `Initialize` parameters.
|
||||
let data_account_keypair = Keypair::new();
|
||||
|
||||
let data_account_key = data_account_keypair.pubkey();
|
||||
|
||||
let data_pda_seeds = &[DataPda::PREFIX.as_ref(), data_account_key.as_ref()];
|
||||
let data_pda_key = Pubkey::find_program_address(data_pda_seeds, &pid).0;
|
||||
let required_keypair = Keypair::new();
|
||||
let value: u64 = 10;
|
||||
|
||||
// Build and send a transaction.
|
||||
|
||||
program
|
||||
.request()
|
||||
.instruction(system_instruction::create_account(
|
||||
&program.payer(),
|
||||
&required_keypair.pubkey(),
|
||||
program
|
||||
.rpc()
|
||||
.get_minimum_balance_for_rent_exemption(DataAccount::LEN)?,
|
||||
DataAccount::LEN as u64,
|
||||
&program.id(),
|
||||
))
|
||||
.signer(&data_account_keypair)
|
||||
.signer(&required_keypair)
|
||||
.accounts(OptionalInitialize {
|
||||
payer: Some(program.payer()),
|
||||
required: required_keypair.pubkey(),
|
||||
system_program: Some(system_program::id()),
|
||||
optional_account: Some(data_account_keypair.pubkey()),
|
||||
optional_pda: None,
|
||||
})
|
||||
.args(optional_instruction::Initialize { value, key: pid })
|
||||
.send()
|
||||
.unwrap();
|
||||
|
||||
// Assert the transaction worked.
|
||||
let required: DataAccount = program.account(required_keypair.pubkey())?;
|
||||
assert_eq!(required.data, 0);
|
||||
|
||||
let optional_pda = program.account::<DataPda>(data_pda_key);
|
||||
assert!(optional_pda.is_err());
|
||||
|
||||
let optional_account: DataAccount = program.account(data_account_keypair.pubkey())?;
|
||||
assert_eq!(optional_account.data, value * 2);
|
||||
|
||||
println!("Optional success!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
//! `anchor_client` provides an RPC client to send transactions and fetch
|
||||
//! deserialized accounts from Solana programs written in `anchor_lang`.
|
||||
|
||||
use anchor_lang::solana_program::hash::Hash;
|
||||
use anchor_lang::solana_program::instruction::{AccountMeta, Instruction};
|
||||
use anchor_lang::solana_program::program_error::ProgramError;
|
||||
use anchor_lang::solana_program::pubkey::Pubkey;
|
||||
use anchor_lang::solana_program::system_program;
|
||||
use anchor_lang::{AccountDeserialize, Discriminator, InstructionData, ToAccountMetas};
|
||||
use regex::Regex;
|
||||
use solana_account_decoder::UiAccountEncoding;
|
||||
|
@ -15,10 +15,9 @@ use solana_client::rpc_config::{
|
|||
RpcAccountInfoConfig, RpcProgramAccountsConfig, RpcSendTransactionConfig,
|
||||
RpcTransactionLogsConfig, RpcTransactionLogsFilter,
|
||||
};
|
||||
use solana_client::rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType};
|
||||
use solana_client::rpc_filter::{Memcmp, RpcFilterType};
|
||||
use solana_client::rpc_response::{Response as RpcResponse, RpcLogsResponse};
|
||||
use solana_sdk::account::Account;
|
||||
use solana_sdk::bs58;
|
||||
use solana_sdk::commitment_config::CommitmentConfig;
|
||||
use solana_sdk::signature::{Signature, Signer};
|
||||
use solana_sdk::transaction::Transaction;
|
||||
|
@ -112,18 +111,6 @@ impl Program {
|
|||
self.cfg.cluster.url(),
|
||||
self.cfg.payer.clone(),
|
||||
self.cfg.options,
|
||||
RequestNamespace::Global,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a request builder for program state.
|
||||
pub fn state_request(&self) -> RequestBuilder {
|
||||
RequestBuilder::from(
|
||||
self.program_id,
|
||||
self.cfg.cluster.url(),
|
||||
self.cfg.payer.clone(),
|
||||
self.cfg.options,
|
||||
RequestNamespace::State { new: false },
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -155,11 +142,8 @@ impl Program {
|
|||
&self,
|
||||
filters: Vec<RpcFilterType>,
|
||||
) -> Result<ProgramAccountsIterator<T>, ClientError> {
|
||||
let account_type_filter = RpcFilterType::Memcmp(Memcmp {
|
||||
offset: 0,
|
||||
bytes: MemcmpEncodedBytes::Base58(bs58::encode(T::discriminator()).into_string()),
|
||||
encoding: None,
|
||||
});
|
||||
let account_type_filter =
|
||||
RpcFilterType::Memcmp(Memcmp::new_base58_encoded(0, &T::discriminator()));
|
||||
let config = RpcProgramAccountsConfig {
|
||||
filters: Some([vec![account_type_filter], filters].concat()),
|
||||
account_config: RpcAccountInfoConfig {
|
||||
|
@ -179,10 +163,6 @@ impl Program {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn state<T: AccountDeserialize>(&self) -> Result<T, ClientError> {
|
||||
self.account(anchor_lang::__private::state::address(&self.program_id))
|
||||
}
|
||||
|
||||
pub fn rpc(&self) -> RpcClient {
|
||||
RpcClient::new_with_commitment(
|
||||
self.cfg.cluster.url().to_string(),
|
||||
|
@ -285,7 +265,7 @@ fn handle_program_log<T: anchor_lang::Event + anchor_lang::AnchorDeserialize>(
|
|||
.strip_prefix(PROGRAM_LOG)
|
||||
.or_else(|| l.strip_prefix(PROGRAM_DATA))
|
||||
{
|
||||
let borsh_bytes = match anchor_lang::__private::base64::decode(&log) {
|
||||
let borsh_bytes = match anchor_lang::__private::base64::decode(log) {
|
||||
Ok(borsh_bytes) => borsh_bytes,
|
||||
_ => {
|
||||
#[cfg(feature = "debug")]
|
||||
|
@ -403,18 +383,6 @@ pub struct RequestBuilder<'a> {
|
|||
// Serialized instruction data for the target RPC.
|
||||
instruction_data: Option<Vec<u8>>,
|
||||
signers: Vec<&'a dyn Signer>,
|
||||
// True if the user is sending a state instruction.
|
||||
namespace: RequestNamespace,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum RequestNamespace {
|
||||
Global,
|
||||
State {
|
||||
// True if the request is to the state's new ctor.
|
||||
new: bool,
|
||||
},
|
||||
Interface,
|
||||
}
|
||||
|
||||
impl<'a> RequestBuilder<'a> {
|
||||
|
@ -423,7 +391,6 @@ impl<'a> RequestBuilder<'a> {
|
|||
cluster: &str,
|
||||
payer: Rc<dyn Signer>,
|
||||
options: Option<CommitmentConfig>,
|
||||
namespace: RequestNamespace,
|
||||
) -> Self {
|
||||
Self {
|
||||
program_id,
|
||||
|
@ -434,7 +401,6 @@ impl<'a> RequestBuilder<'a> {
|
|||
instructions: Vec::new(),
|
||||
instruction_data: None,
|
||||
signers: Vec::new(),
|
||||
namespace,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -481,16 +447,6 @@ impl<'a> RequestBuilder<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Invokes the `#[state]`'s `new` constructor.
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
#[must_use]
|
||||
pub fn new(mut self, args: impl InstructionData) -> Self {
|
||||
assert!(self.namespace == RequestNamespace::State { new: false });
|
||||
self.namespace = RequestNamespace::State { new: true };
|
||||
self.instruction_data = Some(args.data());
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn signer(mut self, signer: &'a dyn Signer) -> Self {
|
||||
self.signers.push(signer);
|
||||
|
@ -498,59 +454,54 @@ impl<'a> RequestBuilder<'a> {
|
|||
}
|
||||
|
||||
pub fn instructions(&self) -> Result<Vec<Instruction>, ClientError> {
|
||||
let mut accounts = match self.namespace {
|
||||
RequestNamespace::State { new } => match new {
|
||||
false => vec![AccountMeta::new(
|
||||
anchor_lang::__private::state::address(&self.program_id),
|
||||
false,
|
||||
)],
|
||||
true => vec![
|
||||
AccountMeta::new_readonly(self.payer.pubkey(), true),
|
||||
AccountMeta::new(
|
||||
anchor_lang::__private::state::address(&self.program_id),
|
||||
false,
|
||||
),
|
||||
AccountMeta::new_readonly(
|
||||
Pubkey::find_program_address(&[], &self.program_id).0,
|
||||
false,
|
||||
),
|
||||
AccountMeta::new_readonly(system_program::ID, false),
|
||||
AccountMeta::new_readonly(self.program_id, false),
|
||||
],
|
||||
},
|
||||
_ => Vec::new(),
|
||||
};
|
||||
accounts.extend_from_slice(&self.accounts);
|
||||
|
||||
let mut instructions = self.instructions.clone();
|
||||
if let Some(ix_data) = &self.instruction_data {
|
||||
instructions.push(Instruction {
|
||||
program_id: self.program_id,
|
||||
data: ix_data.clone(),
|
||||
accounts,
|
||||
accounts: self.accounts.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(instructions)
|
||||
}
|
||||
|
||||
pub fn send(self) -> Result<Signature, ClientError> {
|
||||
fn signed_transaction_with_blockhash(
|
||||
&self,
|
||||
latest_hash: Hash,
|
||||
) -> Result<Transaction, ClientError> {
|
||||
let instructions = self.instructions()?;
|
||||
|
||||
let mut signers = self.signers;
|
||||
let mut signers = self.signers.clone();
|
||||
signers.push(&*self.payer);
|
||||
|
||||
let rpc_client = RpcClient::new_with_commitment(self.cluster, self.options);
|
||||
let tx = Transaction::new_signed_with_payer(
|
||||
&instructions,
|
||||
Some(&self.payer.pubkey()),
|
||||
&signers,
|
||||
latest_hash,
|
||||
);
|
||||
|
||||
let tx = {
|
||||
let latest_hash = rpc_client.get_latest_blockhash()?;
|
||||
Transaction::new_signed_with_payer(
|
||||
&instructions,
|
||||
Some(&self.payer.pubkey()),
|
||||
&signers,
|
||||
latest_hash,
|
||||
)
|
||||
};
|
||||
Ok(tx)
|
||||
}
|
||||
|
||||
pub fn signed_transaction(&self) -> Result<Transaction, ClientError> {
|
||||
let latest_hash =
|
||||
RpcClient::new_with_commitment(&self.cluster, self.options).get_latest_blockhash()?;
|
||||
let tx = self.signed_transaction_with_blockhash(latest_hash)?;
|
||||
|
||||
Ok(tx)
|
||||
}
|
||||
|
||||
pub fn transaction(&self) -> Result<Transaction, ClientError> {
|
||||
let instructions = &self.instructions;
|
||||
let tx = Transaction::new_with_payer(instructions, Some(&self.payer.pubkey()));
|
||||
Ok(tx)
|
||||
}
|
||||
|
||||
pub fn send(self) -> Result<Signature, ClientError> {
|
||||
let rpc_client = RpcClient::new_with_commitment(&self.cluster, self.options);
|
||||
let latest_hash = rpc_client.get_latest_blockhash()?;
|
||||
let tx = self.signed_transaction_with_blockhash(latest_hash)?;
|
||||
|
||||
rpc_client
|
||||
.send_and_confirm_transaction(&tx)
|
||||
|
@ -561,22 +512,9 @@ impl<'a> RequestBuilder<'a> {
|
|||
self,
|
||||
config: RpcSendTransactionConfig,
|
||||
) -> Result<Signature, ClientError> {
|
||||
let instructions = self.instructions()?;
|
||||
|
||||
let mut signers = self.signers;
|
||||
signers.push(&*self.payer);
|
||||
|
||||
let rpc_client = RpcClient::new_with_commitment(self.cluster, self.options);
|
||||
|
||||
let tx = {
|
||||
let latest_hash = rpc_client.get_latest_blockhash()?;
|
||||
Transaction::new_signed_with_payer(
|
||||
&instructions,
|
||||
Some(&self.payer.pubkey()),
|
||||
&signers,
|
||||
latest_hash,
|
||||
)
|
||||
};
|
||||
let rpc_client = RpcClient::new_with_commitment(&self.cluster, self.options);
|
||||
let latest_hash = rpc_client.get_latest_blockhash()?;
|
||||
let tx = self.signed_transaction_with_blockhash(latest_hash)?;
|
||||
|
||||
rpc_client
|
||||
.send_and_confirm_transaction_with_spinner_and_config(
|
||||
|
|
|
@ -2,11 +2,11 @@ WORKDIR=$(PWD)
|
|||
#
|
||||
# Extract anchor version from the Cargo.toml.
|
||||
#
|
||||
ANCHOR_CLI=v$(shell awk -F ' = ' '$$1 ~ /version/ { gsub(/[\"]/, "", $$2); printf("%s",$$2) }' ../cli/Cargo.toml)
|
||||
ANCHOR_CLI=v0.26.0
|
||||
#
|
||||
# Solana toolchain.
|
||||
#
|
||||
SOLANA_CLI=v1.10.29
|
||||
SOLANA_CLI=v1.13.5
|
||||
#
|
||||
# Build version should match the Anchor cli version.
|
||||
#
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
"lint": "next lint",
|
||||
"publish:typedoc": "gh-pages -d ./src/.vuepress/dist"
|
||||
},
|
||||
"browserslist": "defaults, not ie <= 11",
|
||||
"dependencies": {
|
||||
|
@ -19,6 +20,7 @@
|
|||
"autoprefixer": "^10.4.7",
|
||||
"clsx": "^1.2.1",
|
||||
"focus-visible": "^5.2.0",
|
||||
"gh-pages": "^4.0.0",
|
||||
"next": "12.2.1",
|
||||
"next-plausible": "^3.2.0",
|
||||
"postcss-focus-visible": "^6.0.4",
|
||||
|
|
|
@ -16,6 +16,6 @@ cpi = ["no-entrypoint"]
|
|||
default = []
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = "=0.24.1"
|
||||
anchor-lang = "=0.26.0"
|
||||
num-traits = "0.2"
|
||||
num-derive = "0.3"
|
||||
|
|
|
@ -20,6 +20,7 @@ FLAGS:
|
|||
-V, --version Prints version information
|
||||
|
||||
SUBCOMMANDS:
|
||||
account Fetch and deserialize an account using the IDL provided
|
||||
build Builds the workspace
|
||||
cluster Cluster commands
|
||||
deploy Deploys each program in the workspace
|
||||
|
@ -37,6 +38,28 @@ SUBCOMMANDS:
|
|||
Cargo.toml
|
||||
```
|
||||
|
||||
## Account
|
||||
|
||||
```
|
||||
anchor account <program-name>.<AccountTypeName> <account_pubkey>
|
||||
```
|
||||
|
||||
Fetches an account with the given public key and deserializes the data to JSON using the type name provided. If this command is run from within a workspace, the workspace's IDL files will be used to get the data types. Otherwise, the path to the IDL file must be provided.
|
||||
|
||||
The `program-name` is the name of the program where the account struct resides, usually under `programs/<program-name>`. `program-name` should be provided in a case-sensitive manner exactly as the folder name, usually in kebab-case.
|
||||
|
||||
The `AccountTypeName` is the name of the account struct, usually in PascalCase.
|
||||
|
||||
The `account_pubkey` refers to the Pubkey of the account to deserialise, in Base58.
|
||||
|
||||
Example Usage: `anchor account anchor-escrow.EscrowAccount 3PNkzWKXCsbjijbasnx55NEpJe8DFXvEEbJKdRKpDcfK`, deserializes an account in the given pubkey with the account struct `EscrowAccount` defined in the `anchor-escrow` program.
|
||||
|
||||
```
|
||||
anchor account <program-name>.<AccountTypeName> <account_pubkey> --idl <path/to/idl.json>
|
||||
```
|
||||
|
||||
Deserializes the account with the data types provided in the given IDL file even if inside a workspace.
|
||||
|
||||
## Build
|
||||
|
||||
```shell
|
||||
|
@ -186,7 +209,7 @@ from the workspace's `Anchor.toml`. For example,
|
|||
```javascript
|
||||
// File: migrations/deploys.js
|
||||
|
||||
const anchor = require('@project-serum/anchor')
|
||||
const anchor = require('@coral-xyz/anchor')
|
||||
|
||||
module.exports = async function (provider) {
|
||||
anchor.setProvider(provider)
|
||||
|
|
|
@ -147,8 +147,8 @@ impl<'info> PullStrings<'info> {
|
|||
We can verify that everything works as expected by replacing the contents of the `puppet.ts` file with:
|
||||
|
||||
```ts
|
||||
import * as anchor from '@project-serum/anchor'
|
||||
import { Program } from '@project-serum/anchor'
|
||||
import * as anchor from '@coral-xyz/anchor'
|
||||
import { Program } from '@coral-xyz/anchor'
|
||||
import { Keypair } from '@solana/web3.js'
|
||||
import { expect } from 'chai'
|
||||
import { Puppet } from '../target/types/puppet'
|
||||
|
@ -286,8 +286,8 @@ impl<'info> PullStrings<'info> {
|
|||
Finally, change the test:
|
||||
|
||||
```ts
|
||||
import * as anchor from '@project-serum/anchor'
|
||||
import { Program } from '@project-serum/anchor'
|
||||
import * as anchor from '@coral-xyz/anchor'
|
||||
import { Program } from '@coral-xyz/anchor'
|
||||
import { Keypair } from '@solana/web3.js'
|
||||
import { Puppet } from '../target/types/puppet'
|
||||
import { PuppetMaster } from '../target/types/puppet_master'
|
||||
|
|
|
@ -32,13 +32,13 @@ Anchor version manager is a tool for using multiple versions of the anchor-cli.
|
|||
Install `avm` using Cargo. Note this will replace your `anchor` binary if you had one installed.
|
||||
|
||||
```shell
|
||||
cargo install --git https://github.com/project-serum/anchor avm --locked --force
|
||||
cargo install --git https://github.com/coral-xyz/anchor avm --locked --force
|
||||
```
|
||||
|
||||
On Linux systems you may need to install additional dependencies if cargo install fails. E.g. on Ubuntu:
|
||||
|
||||
```shell
|
||||
sudo apt-get update && sudo apt-get upgrade && sudo apt-get install -y pkg-config build-essential libudev-dev
|
||||
sudo apt-get update && sudo apt-get upgrade && sudo apt-get install -y pkg-config build-essential libudev-dev libssl-dev
|
||||
```
|
||||
|
||||
Install the latest version of the CLI using `avm`, and then set it to be the version to use.
|
||||
|
@ -56,14 +56,14 @@ anchor --version
|
|||
|
||||
### Install using pre-build binary on x86_64 Linux
|
||||
|
||||
Anchor binaries are available via an NPM package [`@project-serum/anchor-cli`](https://www.npmjs.com/package/@project-serum/anchor-cli). Only `x86_64` Linux is supported currently, you must build from source for other OS'.
|
||||
Anchor binaries are available via an NPM package [`@coral-xyz/anchor-cli`](https://www.npmjs.com/package/@coral-xyz/anchor-cli). Only `x86_64` Linux is supported currently, you must build from source for other OS'.
|
||||
|
||||
### Build from source for other operating systems without avm
|
||||
|
||||
We can also use Cargo to install the CLI directly. Make sure that the `--tag` argument uses the version you want (the version here is just an example).
|
||||
|
||||
```shell
|
||||
cargo install --git https://github.com/project-serum/anchor --tag v0.24.1 anchor-cli --locked
|
||||
cargo install --git https://github.com/coral-xyz/anchor --tag v0.26.0 anchor-cli --locked
|
||||
```
|
||||
|
||||
On Linux systems you may need to install additional dependencies if cargo install fails. On Ubuntu,
|
||||
|
|
|
@ -31,7 +31,7 @@ The first point means that even if in theory the program may read and write to a
|
|||
|
||||
> This design is partly responsible for Solana’s high throughput. The runtime can look at all the incoming transactions of a program (and even across programs) and can check whether the memory regions in the first argument of the transactions overlap. If they don’t, the runtime can run these transactions in parallel because they don’t conflict with each other. Even better, if the runtime sees that two transactions access overlapping memory regions but only read and don’t write, it can also parallelize those transactions because they do not conflict with each other.
|
||||
|
||||
How exactly can a transaction specify a memory region/account? To answer that, we need to look deeper into what properties an account has ([docs here](https://docs.rs/solana-program/latest/solana_program/account_info/struct.AccountInfo.html). This is the data structure for an account in a transaction. The `is_signer` and `is_writable` fields are set per transaction (e.g. `is_signed` is set if the corresponding private key of the account's `key` field signed the transaction) and are not part of the metadata that is saved in the heap). In front of the user data that the account can store (in the `data` field) , there is some metadata connected to each account. First, it has a key property which is a ed25519 public key and serves as the address of the account. This is how the transaction can specify which accounts the program may access in the transaction.
|
||||
How exactly can a transaction specify a memory region/account? To answer that, we need to look deeper into what properties an account has ([docs here](https://docs.rs/solana-program/latest/solana_program/account_info/struct.AccountInfo.html)). This is the data structure for an account in a transaction. The `is_signer` and `is_writable` fields are set per transaction (e.g. `is_signed` is set if the corresponding private key of the account's `key` field signed the transaction) and are not part of the metadata that is saved in the heap). In front of the user data that the account can store (in the `data` field) , there is some metadata connected to each account. First, it has a key property which is a ed25519 public key and serves as the address of the account. This is how the transaction can specify which accounts the program may access in the transaction.
|
||||
|
||||
![Transaction](/transaction.svg)
|
||||
|
||||
|
|
|
@ -37,6 +37,51 @@ Example:
|
|||
url = "https://anchor.projectserum.com"
|
||||
```
|
||||
|
||||
## features
|
||||
|
||||
#### seeds
|
||||
|
||||
This tells the IDL to include seed generation for PDA Accounts. The default is `false`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
[features]
|
||||
seeds = true
|
||||
```
|
||||
|
||||
## workspace
|
||||
|
||||
#### types
|
||||
|
||||
Adds a directory where you want the `<idl>.ts` file to be copied when running `anchor build` or `anchor idl parse`. This is helpful when you want to keep this file in version control, like when using it on the frontend, which will probably not have access to the `target` directory generated by anchor.
|
||||
|
||||
Example:
|
||||
|
||||
```toml
|
||||
[workspace]
|
||||
types = "app/src/idl/"
|
||||
```
|
||||
|
||||
#### members
|
||||
|
||||
Sets the paths --relative to the `Anchor.toml`--
|
||||
to all programs in the local
|
||||
workspace, i.e., the path to the `Cargo.toml` manifest associated with each
|
||||
program that can be compiled by the `anchor` CLI. For programs using the
|
||||
standard Anchor workflow, this can be ommitted. For programs not written in Anchor
|
||||
but still want to publish, this should be added.
|
||||
|
||||
Example:
|
||||
|
||||
```toml
|
||||
[workspace]
|
||||
members = [
|
||||
"programs/*",
|
||||
"other_place/my_program"
|
||||
]
|
||||
```
|
||||
|
||||
## programs
|
||||
|
||||
Example:
|
||||
|
@ -71,7 +116,7 @@ Example
|
|||
|
||||
```toml
|
||||
[[test.genesis]]
|
||||
address = "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"
|
||||
address = "srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX"
|
||||
program = "dex.so"
|
||||
|
||||
[[test.genesis]]
|
||||
|
|
|
@ -190,8 +190,8 @@ pub fn change_user_name(ctx: Context<ChangeUserName>, new_name: String) -> Resul
|
|||
Finally, let's add a test. Copy this into `game.ts`
|
||||
|
||||
```ts
|
||||
import * as anchor from '@project-serum/anchor'
|
||||
import { Program } from '@project-serum/anchor'
|
||||
import * as anchor from '@coral-xyz/anchor'
|
||||
import { Program } from '@coral-xyz/anchor'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
import { Game } from '../target/types/game'
|
||||
import { expect } from 'chai'
|
||||
|
@ -306,8 +306,8 @@ The `authority` account is now an `UncheckedAccount` instead of a `Signer`. When
|
|||
Finally, this is the new `puppet.ts`:
|
||||
|
||||
```ts
|
||||
import * as anchor from '@project-serum/anchor'
|
||||
import { Program } from '@project-serum/anchor'
|
||||
import * as anchor from '@coral-xyz/anchor'
|
||||
import { Program } from '@coral-xyz/anchor'
|
||||
import { Keypair, PublicKey } from '@solana/web3.js'
|
||||
import { Puppet } from '../target/types/puppet'
|
||||
import { PuppetMaster } from '../target/types/puppet_master'
|
||||
|
|
|
@ -34,7 +34,7 @@ have an `Anchor.toml` to define the build.
|
|||
An example `Anchor.toml` config looks as follows,
|
||||
|
||||
```toml
|
||||
anchor_version = "0.25.0"
|
||||
anchor_version = "0.26.0"
|
||||
|
||||
[workspace]
|
||||
members = ["programs/multisig"]
|
||||
|
|
|
@ -8,7 +8,7 @@ The minor version will be incremented upon a breaking change and the patch versi
|
|||
|
||||
---
|
||||
|
||||
## [0.25.0] - 2022-07-05
|
||||
## [0.26.0] - 2022-07-05
|
||||
|
||||
### Features
|
||||
|
||||
|
|
|
@ -169,8 +169,6 @@ pub struct Initialize<'info> {
|
|||
}
|
||||
```
|
||||
|
||||
Note the doc comment needs to be a [line or block doc comment](https://doc.rust-lang.org/reference/comments.html#doc-comments) (/// or /\*\*) to be interepreted as doc attribute by Rust. Double slash comments (//) are not interpreted as such.
|
||||
|
||||
{% callout type="warning" title="Note" %}
|
||||
The doc comment needs to be a [line or block doc comment](https://doc.rust-lang.org/reference/comments.html#doc-comments) (/// or /\*\*) to be interepreted as doc attribute by Rust. Double slash comments (//) are not interpreted as such.
|
||||
{% /callout %}
|
||||
|
|
|
@ -416,7 +416,7 @@ You can create then a new `it` test, setup the game like in the previous test, b
|
|||
```typescript
|
||||
it('player one wins', async () => {
|
||||
const gameKeypair = anchor.web3.Keypair.generate()
|
||||
const playerOne = program.provider.wallet
|
||||
const playerOne = (program.provider as anchor.AnchorProvider).wallet
|
||||
const playerTwo = anchor.web3.Keypair.generate()
|
||||
await program.methods
|
||||
.setupGame(playerTwo.publicKey)
|
||||
|
@ -455,7 +455,7 @@ it('player one wins', async () => {
|
|||
|
||||
and run `anchor test`.
|
||||
|
||||
You can finish writing the test by yourself (or check out [the reference implementation](https://github.com/project-serum/anchor-book/tree/master/programs/tic-tac-toe)). Try to simulate a win and a tie!
|
||||
You can finish writing the test by yourself (or check out [the reference implementation](https://github.com/coral-xyz/anchor/tree/master/docs/programs/tic-tac-toe)). Try to simulate a win and a tie!
|
||||
|
||||
Proper testing also includes tests that try to exploit the contract. You can check whether you've protected yourself properly by calling `play` with unexpected parameters. You can also familiarize yourself with the returned `AnchorErrors` this way. For example:
|
||||
|
||||
|
@ -538,7 +538,7 @@ There is more to deployments than this e.g. understanding how the BPFLoader work
|
|||
|
||||
## Program directory organization
|
||||
|
||||
> [Program Code](https://github.com/project-serum/anchor-book/tree/master/programs/tic-tac-toe)
|
||||
> [Program Code](https://github.com/coral-xyz/anchor/tree/master/docs/programs/tic-tac-toe)
|
||||
|
||||
Eventually, some programs become too big to keep them in a single file and it makes sense to break them up.
|
||||
|
||||
|
|
|
@ -37,10 +37,10 @@ If the program has an IDL, it will also check the IDL deployed on chain matches.
|
|||
|
||||
## Images
|
||||
|
||||
A docker image for each version of Anchor is published on [Docker Hub](https://hub.docker.com/r/projectserum/build). They are tagged in the form `projectserum/build:<version>`. For example, to get the image for Anchor `v0.25.0` one can run
|
||||
A docker image for each version of Anchor is published on [Docker Hub](https://hub.docker.com/r/projectserum/build). They are tagged in the form `projectserum/build:<version>`. For example, to get the image for Anchor `v0.26.0` one can run
|
||||
|
||||
```shell
|
||||
docker pull projectserum/build:v0.25.0
|
||||
docker pull projectserum/build:v0.26.0
|
||||
```
|
||||
|
||||
## Removing an Image
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
#!/bin/bash
|
||||
|
||||
git diff --quiet HEAD^ HEAD ./
|
||||
has_changes=$?
|
||||
echo ">> Diff status $has_changes"
|
||||
|
||||
if [[ $VERCEL_GIT_COMMIT_REF == "master" ]] || [ $has_changes == 1 ]; then
|
||||
echo ">> Proceeding with deployment."
|
||||
exit 1;
|
||||
else
|
||||
echo ">> Skipping deployment."
|
||||
exit 0;
|
||||
fi
|
|
@ -2,7 +2,7 @@
|
|||
// It is not expected users directly test with this example. For a more
|
||||
// ergonomic example, see `tests/basic-0.js` in this workspace.
|
||||
|
||||
const anchor = require("@project-serum/anchor");
|
||||
const anchor = require("@coral-xyz/anchor");
|
||||
|
||||
// Configure the local cluster.
|
||||
anchor.setProvider(anchor.AnchorProvider.local());
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "basic-0",
|
||||
"version": "0.25.0",
|
||||
"version": "0.26.0",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/coral-xyz/anchor#readme",
|
||||
"bugs": {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
name = "basic-0"
|
||||
version = "0.1.0"
|
||||
description = "Created with Anchor"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const anchor = require("@project-serum/anchor");
|
||||
const anchor = require("@coral-xyz/anchor");
|
||||
|
||||
describe("basic-0", () => {
|
||||
// Configure the client to use the local cluster.
|
||||
|
@ -10,7 +10,7 @@ describe("basic-0", () => {
|
|||
const program = anchor.workspace.Basic0;
|
||||
|
||||
// Execute the RPC.
|
||||
await program.rpc.initialize();
|
||||
await program.methods.initialize().rpc();
|
||||
// #endregion code
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "basic-1",
|
||||
"version": "0.25.0",
|
||||
"version": "0.26.0",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/coral-xyz/anchor#readme",
|
||||
"bugs": {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
name = "basic-1"
|
||||
version = "0.1.0"
|
||||
description = "Created with Anchor"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const assert = require("assert");
|
||||
const anchor = require("@project-serum/anchor");
|
||||
const anchor = require("@coral-xyz/anchor");
|
||||
const { SystemProgram } = anchor.web3;
|
||||
|
||||
describe("basic-1", () => {
|
||||
|
@ -19,14 +19,15 @@ describe("basic-1", () => {
|
|||
|
||||
// Create the new account and initialize it with the program.
|
||||
// #region code-simplified
|
||||
await program.rpc.initialize(new anchor.BN(1234), {
|
||||
accounts: {
|
||||
await program.methods
|
||||
.initialize(new anchor.BN(1234))
|
||||
.accounts({
|
||||
myAccount: myAccount.publicKey,
|
||||
user: provider.wallet.publicKey,
|
||||
systemProgram: SystemProgram.programId,
|
||||
},
|
||||
signers: [myAccount],
|
||||
});
|
||||
})
|
||||
.signers([myAccount])
|
||||
.rpc();
|
||||
// #endregion code-simplified
|
||||
|
||||
// Fetch the newly created account from the cluster.
|
||||
|
@ -48,11 +49,12 @@ describe("basic-1", () => {
|
|||
const program = anchor.workspace.Basic1;
|
||||
|
||||
// Invoke the update rpc.
|
||||
await program.rpc.update(new anchor.BN(4321), {
|
||||
accounts: {
|
||||
await program.methods
|
||||
.update(new anchor.BN(4321))
|
||||
.accounts({
|
||||
myAccount: myAccount.publicKey,
|
||||
},
|
||||
});
|
||||
})
|
||||
.rpc();
|
||||
|
||||
// Fetch the newly updated account.
|
||||
const account = await program.account.myAccount.fetch(myAccount.publicKey);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "basic-2",
|
||||
"version": "0.25.0",
|
||||
"version": "0.26.0",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/coral-xyz/anchor#readme",
|
||||
"bugs": {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
name = "basic-2"
|
||||
version = "0.1.0"
|
||||
description = "Created with Anchor"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const assert = require("assert");
|
||||
const anchor = require("@project-serum/anchor");
|
||||
const anchor = require("@coral-xyz/anchor");
|
||||
const { SystemProgram } = anchor.web3;
|
||||
|
||||
describe("basic-2", () => {
|
||||
|
@ -15,14 +15,15 @@ describe("basic-2", () => {
|
|||
const program = anchor.workspace.Basic2;
|
||||
|
||||
it("Creates a counter", async () => {
|
||||
await program.rpc.create(provider.wallet.publicKey, {
|
||||
accounts: {
|
||||
await program.methods
|
||||
.create(provider.wallet.publicKey)
|
||||
.accounts({
|
||||
counter: counter.publicKey,
|
||||
user: provider.wallet.publicKey,
|
||||
systemProgram: SystemProgram.programId,
|
||||
},
|
||||
signers: [counter],
|
||||
});
|
||||
})
|
||||
.signers([counter])
|
||||
.rpc();
|
||||
|
||||
let counterAccount = await program.account.counter.fetch(counter.publicKey);
|
||||
|
||||
|
@ -31,12 +32,13 @@ describe("basic-2", () => {
|
|||
});
|
||||
|
||||
it("Updates a counter", async () => {
|
||||
await program.rpc.increment({
|
||||
accounts: {
|
||||
await program.methods
|
||||
.increment()
|
||||
.accounts({
|
||||
counter: counter.publicKey,
|
||||
authority: provider.wallet.publicKey,
|
||||
},
|
||||
});
|
||||
})
|
||||
.rpc();
|
||||
|
||||
const counterAccount = await program.account.counter.fetch(
|
||||
counter.publicKey
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "basic-3",
|
||||
"version": "0.25.0",
|
||||
"version": "0.26.0",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/coral-xyz/anchor#readme",
|
||||
"bugs": {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
name = "puppet-master"
|
||||
version = "0.1.0"
|
||||
description = "Created with Anchor"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
name = "puppet"
|
||||
version = "0.1.0"
|
||||
description = "Created with Anchor"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const assert = require("assert");
|
||||
const anchor = require("@project-serum/anchor");
|
||||
const anchor = require("@coral-xyz/anchor");
|
||||
const { SystemProgram } = anchor.web3;
|
||||
|
||||
describe("basic-3", () => {
|
||||
|
@ -14,22 +14,24 @@ describe("basic-3", () => {
|
|||
|
||||
// Initialize a new puppet account.
|
||||
const newPuppetAccount = anchor.web3.Keypair.generate();
|
||||
const tx = await puppet.rpc.initialize({
|
||||
accounts: {
|
||||
const tx = await puppet.methods
|
||||
.initialize()
|
||||
.accounts({
|
||||
puppet: newPuppetAccount.publicKey,
|
||||
user: provider.wallet.publicKey,
|
||||
systemProgram: SystemProgram.programId,
|
||||
},
|
||||
signers: [newPuppetAccount],
|
||||
});
|
||||
})
|
||||
.signers([newPuppetAccount])
|
||||
.rpc();
|
||||
|
||||
// Invoke the puppet master to perform a CPI to the puppet.
|
||||
await puppetMaster.rpc.pullStrings(new anchor.BN(111), {
|
||||
accounts: {
|
||||
await puppetMaster.methods
|
||||
.pullStrings(new anchor.BN(111))
|
||||
.accounts({
|
||||
puppet: newPuppetAccount.publicKey,
|
||||
puppetProgram: puppet.programId,
|
||||
},
|
||||
});
|
||||
})
|
||||
.rpc();
|
||||
|
||||
// Check the state updated.
|
||||
puppetAccount = await puppet.account.data.fetch(newPuppetAccount.publicKey);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "basic-4",
|
||||
"version": "0.25.0",
|
||||
"version": "0.26.0",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"homepage": "https://github.com/coral-xyz/anchor#readme",
|
||||
"bugs": {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
name = "basic-4"
|
||||
version = "0.1.0"
|
||||
description = "Created with Anchor"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// #region code
|
||||
use anchor_lang::prelude::*;
|
||||
use std::ops::DerefMut;
|
||||
|
||||
declare_id!("CwrqeMj2U8tFr1Rhkgwc84tpAsqbt9pTt2a4taoTADPr");
|
||||
|
||||
|
@ -7,38 +7,72 @@ declare_id!("CwrqeMj2U8tFr1Rhkgwc84tpAsqbt9pTt2a4taoTADPr");
|
|||
pub mod basic_4 {
|
||||
use super::*;
|
||||
|
||||
#[state]
|
||||
pub struct Counter {
|
||||
pub authority: Pubkey,
|
||||
pub count: u64,
|
||||
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
|
||||
let counter = ctx.accounts.counter.deref_mut();
|
||||
let bump = *ctx.bumps.get("counter").ok_or(ErrorCode::CannotGetBump)?;
|
||||
|
||||
*counter = Counter {
|
||||
authority: *ctx.accounts.authority.key,
|
||||
count: 0,
|
||||
bump,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Counter {
|
||||
pub fn new(ctx: Context<Auth>) -> anchor_lang::Result<Self> {
|
||||
Ok(Self {
|
||||
authority: *ctx.accounts.authority.key,
|
||||
count: 0,
|
||||
})
|
||||
}
|
||||
pub fn increment(ctx: Context<Increment>) -> Result<()> {
|
||||
require_keys_eq!(
|
||||
ctx.accounts.authority.key(),
|
||||
ctx.accounts.counter.authority,
|
||||
ErrorCode::Unauthorized
|
||||
);
|
||||
|
||||
pub fn increment(&mut self, ctx: Context<Auth>) -> anchor_lang::Result<()> {
|
||||
if &self.authority != ctx.accounts.authority.key {
|
||||
return Err(error!(ErrorCode::Unauthorized));
|
||||
}
|
||||
self.count += 1;
|
||||
Ok(())
|
||||
}
|
||||
ctx.accounts.counter.count += 1;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct Auth<'info> {
|
||||
pub struct Initialize<'info> {
|
||||
#[account(
|
||||
init,
|
||||
payer = authority,
|
||||
space = Counter::SIZE,
|
||||
seeds = [b"counter"],
|
||||
bump
|
||||
)]
|
||||
counter: Account<'info, Counter>,
|
||||
#[account(mut)]
|
||||
authority: Signer<'info>,
|
||||
system_program: Program<'info, System>,
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct Increment<'info> {
|
||||
#[account(
|
||||
mut,
|
||||
seeds = [b"counter"],
|
||||
bump = counter.bump
|
||||
)]
|
||||
counter: Account<'info, Counter>,
|
||||
authority: Signer<'info>,
|
||||
}
|
||||
// #endregion code
|
||||
|
||||
#[account]
|
||||
pub struct Counter {
|
||||
pub authority: Pubkey,
|
||||
pub count: u64,
|
||||
pub bump: u8,
|
||||
}
|
||||
|
||||
impl Counter {
|
||||
pub const SIZE: usize = 8 + 32 + 8 + 1;
|
||||
}
|
||||
|
||||
#[error_code]
|
||||
pub enum ErrorCode {
|
||||
#[msg("You are not authorized to perform this action.")]
|
||||
Unauthorized,
|
||||
#[msg("Cannot get the bump.")]
|
||||
CannotGetBump,
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const assert = require("assert");
|
||||
const anchor = require("@project-serum/anchor");
|
||||
const anchor = require("@coral-xyz/anchor");
|
||||
|
||||
describe("basic-4", () => {
|
||||
const provider = anchor.AnchorProvider.local();
|
||||
|
@ -7,35 +7,45 @@ describe("basic-4", () => {
|
|||
// Configure the client to use the local cluster.
|
||||
anchor.setProvider(provider);
|
||||
|
||||
const program = anchor.workspace.Basic4;
|
||||
const program = anchor.workspace.Basic4,
|
||||
counterSeed = anchor.utils.bytes.utf8.encode("counter");
|
||||
|
||||
let counterPubkey;
|
||||
|
||||
before(async () => {
|
||||
[counterPubkey] = await anchor.web3.PublicKey.findProgramAddress(
|
||||
[counterSeed],
|
||||
program.programId
|
||||
);
|
||||
});
|
||||
|
||||
it("Is runs the constructor", async () => {
|
||||
// #region ctor
|
||||
// Initialize the program's state struct.
|
||||
await program.state.rpc.new({
|
||||
accounts: {
|
||||
await program.methods
|
||||
.initialize()
|
||||
.accounts({
|
||||
counter: counterPubkey,
|
||||
authority: provider.wallet.publicKey,
|
||||
},
|
||||
});
|
||||
// #endregion ctor
|
||||
systemProgram: anchor.web3.SystemProgram.programId,
|
||||
})
|
||||
.rpc();
|
||||
|
||||
// Fetch the state struct from the network.
|
||||
// #region accessor
|
||||
const state = await program.state.fetch();
|
||||
// #endregion accessor
|
||||
const counterAccount = await program.account.counter.fetch(counterPubkey);
|
||||
|
||||
assert.ok(state.count.eq(new anchor.BN(0)));
|
||||
assert.ok(counterAccount.count.eq(new anchor.BN(0)));
|
||||
});
|
||||
|
||||
it("Executes a method on the program", async () => {
|
||||
// #region instruction
|
||||
await program.state.rpc.increment({
|
||||
accounts: {
|
||||
await program.methods
|
||||
.increment()
|
||||
.accounts({
|
||||
counter: counterPubkey,
|
||||
authority: provider.wallet.publicKey,
|
||||
},
|
||||
});
|
||||
// #endregion instruction
|
||||
const state = await program.state.fetch();
|
||||
assert.ok(state.count.eq(new anchor.BN(1)));
|
||||
})
|
||||
.rpc();
|
||||
|
||||
const counterAccount = await program.account.counter.fetch(counterPubkey);
|
||||
assert.ok(counterAccount.count.eq(new anchor.BN(1)));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"basic-4"
|
||||
],
|
||||
"dependencies": {
|
||||
"@project-serum/anchor": "file:../../ts"
|
||||
"@coral-xyz/anchor": "file:../../ts/packages/anchor"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "^9.1.3",
|
||||
|
|
|
@ -2,44 +2,30 @@
|
|||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5":
|
||||
"@babel/runtime@^7.12.5":
|
||||
version "7.16.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5"
|
||||
integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@ethersproject/bytes@^5.5.0":
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c"
|
||||
integrity sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==
|
||||
"@babel/runtime@^7.17.2":
|
||||
version "7.20.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.6.tgz#facf4879bfed9b5326326273a64220f099b0fce3"
|
||||
integrity sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==
|
||||
dependencies:
|
||||
"@ethersproject/logger" "^5.5.0"
|
||||
regenerator-runtime "^0.13.11"
|
||||
|
||||
"@ethersproject/logger@^5.5.0":
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d"
|
||||
integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==
|
||||
|
||||
"@ethersproject/sha2@^5.5.0":
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.5.0.tgz#a40a054c61f98fd9eee99af2c3cc6ff57ec24db7"
|
||||
integrity sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA==
|
||||
"@coral-xyz/anchor@file:../../ts/packages/anchor":
|
||||
version "0.26.0"
|
||||
dependencies:
|
||||
"@ethersproject/bytes" "^5.5.0"
|
||||
"@ethersproject/logger" "^5.5.0"
|
||||
hash.js "1.1.7"
|
||||
|
||||
"@project-serum/anchor@file:../../ts":
|
||||
version "0.25.0"
|
||||
dependencies:
|
||||
"@project-serum/borsh" "^0.2.5"
|
||||
"@solana/web3.js" "^1.36.0"
|
||||
"@coral-xyz/borsh" "^0.26.0"
|
||||
"@solana/web3.js" "^1.68.0"
|
||||
base64-js "^1.5.1"
|
||||
bn.js "^5.1.2"
|
||||
bs58 "^4.0.1"
|
||||
buffer-layout "^1.2.2"
|
||||
camelcase "^5.3.1"
|
||||
camelcase "^6.3.0"
|
||||
cross-fetch "^3.1.5"
|
||||
crypto-hash "^1.3.0"
|
||||
eventemitter3 "^4.0.7"
|
||||
|
@ -49,47 +35,56 @@
|
|||
superstruct "^0.15.4"
|
||||
toml "^3.0.0"
|
||||
|
||||
"@project-serum/borsh@^0.2.5":
|
||||
version "0.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.5.tgz#6059287aa624ecebbfc0edd35e4c28ff987d8663"
|
||||
integrity sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q==
|
||||
"@coral-xyz/borsh@^0.26.0":
|
||||
version "0.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@coral-xyz/borsh/-/borsh-0.26.0.tgz#d054f64536d824634969e74138f9f7c52bbbc0d5"
|
||||
integrity sha512-uCZ0xus0CszQPHYfWAqKS5swS1UxvePu83oOF+TWpUkedsNlg6p2p4azxZNSSqwXb9uXMFgxhuMBX9r3Xoi0vQ==
|
||||
dependencies:
|
||||
bn.js "^5.1.2"
|
||||
buffer-layout "^1.2.0"
|
||||
|
||||
"@solana/buffer-layout@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-3.0.0.tgz#b9353caeb9a1589cb77a1b145bcb1a9a93114326"
|
||||
integrity sha512-MVdgAKKL39tEs0l8je0hKaXLQFb7Rdfb0Xg2LjFZd8Lfdazkg6xiS98uAZrEKvaoF3i4M95ei9RydkGIDMeo3w==
|
||||
"@noble/ed25519@^1.7.0":
|
||||
version "1.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.1.tgz#6899660f6fbb97798a6fbd227227c4589a454724"
|
||||
integrity sha512-Rk4SkJFaXZiznFyC/t77Q0NKS4FL7TLJJsVG2V2oiEq3kJVeTdxysEe/yRWSpnWMe808XRDJ+VFh5pt/FN5plw==
|
||||
|
||||
"@noble/hashes@^1.1.2":
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.4.tgz#2611ebf5764c1bf754da7c7794de4fb30512336d"
|
||||
integrity sha512-+PYsVPrTSqtVjatKt2A/Proukn2Yrz61OBThOCKErc5w2/r1Fh37vbDv0Eah7pyNltrmacjwTvdw3JoR+WE4TA==
|
||||
|
||||
"@noble/secp256k1@^1.6.3":
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.0.tgz#d15357f7c227e751d90aa06b05a0e5cf993ba8c1"
|
||||
integrity sha512-kbacwGSsH/CTout0ZnZWxnW1B+jH/7r/WAAKLBtrRJ/+CUH7lgmQzl3GTrQua3SGKWNSDsS6lmjnDpIJ5Dxyaw==
|
||||
|
||||
"@solana/buffer-layout@^4.0.0":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz#b996235eaec15b1e0b5092a8ed6028df77fa6c15"
|
||||
integrity sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==
|
||||
dependencies:
|
||||
buffer "~6.0.3"
|
||||
|
||||
"@solana/web3.js@^1.36.0":
|
||||
version "1.36.0"
|
||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.36.0.tgz#79d7d5217b49b80139f4de68953adc5b9a9a264f"
|
||||
integrity sha512-RNT1451iRR7TyW7EJKMCrH/0OXawIe4zVm0DWQASwXlR/u1jmW6FrmH0lujIh7cGTlfOVbH+2ZU9AVUPLBFzwA==
|
||||
"@solana/web3.js@^1.68.0":
|
||||
version "1.70.1"
|
||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.70.1.tgz#4a2df47cc32a0f67be5161e772b2ceb6512281fa"
|
||||
integrity sha512-AnaqCF1cJ3w7d0yhvLGAKAcRI+n5o+ursQihhoTe4cUh8/9d4gbT73SoHYElS7e67OtAgLmSfbcC5hcOAgdvnQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
"@ethersproject/sha2" "^5.5.0"
|
||||
"@solana/buffer-layout" "^3.0.0"
|
||||
"@noble/ed25519" "^1.7.0"
|
||||
"@noble/hashes" "^1.1.2"
|
||||
"@noble/secp256k1" "^1.6.3"
|
||||
"@solana/buffer-layout" "^4.0.0"
|
||||
bigint-buffer "^1.1.5"
|
||||
bn.js "^5.0.0"
|
||||
borsh "^0.4.0"
|
||||
borsh "^0.7.0"
|
||||
bs58 "^4.0.1"
|
||||
buffer "6.0.1"
|
||||
cross-fetch "^3.1.4"
|
||||
fast-stable-stringify "^1.0.0"
|
||||
jayson "^3.4.4"
|
||||
js-sha3 "^0.8.0"
|
||||
rpc-websockets "^7.4.2"
|
||||
secp256k1 "^4.0.2"
|
||||
node-fetch "2"
|
||||
rpc-websockets "^7.5.0"
|
||||
superstruct "^0.14.2"
|
||||
tweetnacl "^1.0.0"
|
||||
|
||||
"@types/bn.js@^4.11.5":
|
||||
version "4.11.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c"
|
||||
integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/connect@^3.4.33":
|
||||
version "3.4.35"
|
||||
|
@ -199,28 +194,41 @@ base64-js@^1.3.1, base64-js@^1.5.1:
|
|||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
|
||||
bigint-buffer@^1.1.5:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442"
|
||||
integrity sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==
|
||||
dependencies:
|
||||
bindings "^1.3.0"
|
||||
|
||||
binary-extensions@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
||||
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
|
||||
|
||||
bn.js@^4.11.9:
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
|
||||
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
|
||||
bindings@^1.3.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
|
||||
integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
|
||||
dependencies:
|
||||
file-uri-to-path "1.0.0"
|
||||
|
||||
bn.js@^5.0.0, bn.js@^5.1.2:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002"
|
||||
integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==
|
||||
|
||||
borsh@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.4.0.tgz#9dd6defe741627f1315eac2a73df61421f6ddb9f"
|
||||
integrity sha512-aX6qtLya3K0AkT66CmYWCCDr77qsE9arV05OmdFpmat9qu8Pg9J5tBUPDztAW5fNh/d/MyVG/OYziP52Ndzx1g==
|
||||
bn.js@^5.2.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70"
|
||||
integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==
|
||||
|
||||
borsh@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a"
|
||||
integrity sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==
|
||||
dependencies:
|
||||
"@types/bn.js" "^4.11.5"
|
||||
bn.js "^5.0.0"
|
||||
bn.js "^5.2.0"
|
||||
bs58 "^4.0.0"
|
||||
text-encoding-utf-8 "^1.0.2"
|
||||
|
||||
|
@ -239,11 +247,6 @@ braces@~3.0.2:
|
|||
dependencies:
|
||||
fill-range "^7.0.1"
|
||||
|
||||
brorand@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
|
||||
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
|
||||
|
||||
browser-stdout@1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
|
||||
|
@ -284,16 +287,16 @@ bufferutil@^4.0.1:
|
|||
dependencies:
|
||||
node-gyp-build "^4.3.0"
|
||||
|
||||
camelcase@^5.3.1:
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
|
||||
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
|
||||
|
||||
camelcase@^6.0.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809"
|
||||
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
|
||||
|
||||
camelcase@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
|
||||
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
|
||||
|
||||
chalk@^4.1.0:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||
|
@ -317,11 +320,6 @@ chokidar@3.5.2:
|
|||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
circular-json@^0.5.9:
|
||||
version "0.5.9"
|
||||
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.9.tgz#932763ae88f4f7dead7a0d09c8a51a4743a53b1d"
|
||||
integrity sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==
|
||||
|
||||
cliui@^7.0.2:
|
||||
version "7.0.4"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
|
||||
|
@ -353,13 +351,6 @@ concat-map@0.0.1:
|
|||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
cross-fetch@^3.1.4:
|
||||
version "3.1.4"
|
||||
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39"
|
||||
integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==
|
||||
dependencies:
|
||||
node-fetch "2.6.1"
|
||||
|
||||
cross-fetch@^3.1.5:
|
||||
version "3.1.5"
|
||||
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
|
||||
|
@ -402,19 +393,6 @@ dot-case@^3.0.4:
|
|||
no-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
|
||||
elliptic@^6.5.2:
|
||||
version "6.5.4"
|
||||
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
|
||||
integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==
|
||||
dependencies:
|
||||
bn.js "^4.11.9"
|
||||
brorand "^1.1.0"
|
||||
hash.js "^1.0.0"
|
||||
hmac-drbg "^1.0.1"
|
||||
inherits "^2.0.4"
|
||||
minimalistic-assert "^1.0.1"
|
||||
minimalistic-crypto-utils "^1.0.1"
|
||||
|
||||
emoji-regex@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||
|
@ -452,6 +430,16 @@ eyes@^0.1.8:
|
|||
resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0"
|
||||
integrity sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=
|
||||
|
||||
fast-stable-stringify@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz#5c5543462b22aeeefd36d05b34e51c78cb86d313"
|
||||
integrity sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==
|
||||
|
||||
file-uri-to-path@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
|
||||
integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
|
||||
|
||||
fill-range@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
|
||||
|
@ -516,28 +504,11 @@ has-flag@^4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
|
||||
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
|
||||
|
||||
hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3:
|
||||
version "1.1.7"
|
||||
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
|
||||
integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
|
||||
dependencies:
|
||||
inherits "^2.0.3"
|
||||
minimalistic-assert "^1.0.1"
|
||||
|
||||
he@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||
|
||||
hmac-drbg@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
|
||||
integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=
|
||||
dependencies:
|
||||
hash.js "^1.0.3"
|
||||
minimalistic-assert "^1.0.0"
|
||||
minimalistic-crypto-utils "^1.0.1"
|
||||
|
||||
ieee754@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||
|
@ -551,7 +522,7 @@ inflight@^1.0.4:
|
|||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2, inherits@^2.0.3, inherits@^2.0.4:
|
||||
inherits@2:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
@ -631,11 +602,6 @@ js-sha256@^0.9.0:
|
|||
resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966"
|
||||
integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==
|
||||
|
||||
js-sha3@^0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
|
||||
integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
|
||||
|
||||
js-yaml@4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
|
||||
|
@ -680,16 +646,6 @@ lower-case@^2.0.2:
|
|||
dependencies:
|
||||
tslib "^2.0.3"
|
||||
|
||||
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
|
||||
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
|
||||
|
||||
minimalistic-crypto-utils@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
|
||||
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
|
||||
|
||||
minimatch@3.0.4, minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
|
@ -750,24 +706,14 @@ no-case@^3.0.4:
|
|||
lower-case "^2.0.2"
|
||||
tslib "^2.0.3"
|
||||
|
||||
node-addon-api@^2.0.0:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32"
|
||||
integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==
|
||||
|
||||
node-fetch@2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
|
||||
node-fetch@2.6.7:
|
||||
node-fetch@2, node-fetch@2.6.7:
|
||||
version "2.6.7"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
|
||||
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
|
||||
dependencies:
|
||||
whatwg-url "^5.0.0"
|
||||
|
||||
node-gyp-build@^4.2.0, node-gyp-build@^4.3.0:
|
||||
node-gyp-build@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3"
|
||||
integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==
|
||||
|
@ -837,6 +783,11 @@ readdirp@~3.6.0:
|
|||
dependencies:
|
||||
picomatch "^2.2.1"
|
||||
|
||||
regenerator-runtime@^0.13.11:
|
||||
version "0.13.11"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
|
||||
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
|
||||
|
||||
regenerator-runtime@^0.13.4:
|
||||
version "0.13.9"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
|
||||
|
@ -847,16 +798,15 @@ require-directory@^2.1.1:
|
|||
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
||||
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
|
||||
|
||||
rpc-websockets@^7.4.2:
|
||||
version "7.4.16"
|
||||
resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.4.16.tgz#eb701cdef577d4357ba5f526d50e25f370396fac"
|
||||
integrity sha512-0b7OVhutzwRIaYAtJo5tqtaQTWKfwAsKnaThOSOy+VkhVdleNUgb8eZnWSdWITRZZEigV5uPEIDr5KZe4DBrdQ==
|
||||
rpc-websockets@^7.5.0:
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.5.0.tgz#bbeb87572e66703ff151e50af1658f98098e2748"
|
||||
integrity sha512-9tIRi1uZGy7YmDjErf1Ax3wtqdSSLIlnmL5OtOzgd5eqPKbsPpwDP5whUDO2LQay3Xp0CcHlcNSGzacNRluBaQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.11.2"
|
||||
circular-json "^0.5.9"
|
||||
"@babel/runtime" "^7.17.2"
|
||||
eventemitter3 "^4.0.7"
|
||||
uuid "^8.3.0"
|
||||
ws "^7.4.5"
|
||||
uuid "^8.3.2"
|
||||
ws "^8.5.0"
|
||||
optionalDependencies:
|
||||
bufferutil "^4.0.1"
|
||||
utf-8-validate "^5.0.2"
|
||||
|
@ -866,15 +816,6 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0:
|
|||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
||||
secp256k1@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.2.tgz#15dd57d0f0b9fdb54ac1fa1694f40e5e9a54f4a1"
|
||||
integrity sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==
|
||||
dependencies:
|
||||
elliptic "^6.5.2"
|
||||
node-addon-api "^2.0.0"
|
||||
node-gyp-build "^4.2.0"
|
||||
|
||||
serialize-javascript@6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
|
||||
|
@ -967,11 +908,6 @@ tslib@^2.0.3:
|
|||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
|
||||
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
|
||||
|
||||
tweetnacl@^1.0.0:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
|
||||
integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
|
||||
|
||||
utf-8-validate@^5.0.2:
|
||||
version "5.0.7"
|
||||
resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.7.tgz#c15a19a6af1f7ad9ec7ddc425747ca28c3644922"
|
||||
|
@ -984,7 +920,7 @@ uuid@^3.4.0:
|
|||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||
|
||||
uuid@^8.3.0:
|
||||
uuid@^8.3.2:
|
||||
version "8.3.2"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
|
@ -1033,6 +969,11 @@ ws@^7.4.5:
|
|||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881"
|
||||
integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==
|
||||
|
||||
ws@^8.5.0:
|
||||
version "8.11.0"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143"
|
||||
integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==
|
||||
|
||||
y18n@^5.0.5:
|
||||
version "5.0.8"
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
[package]
|
||||
name = "anchor-lang"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/coral-xyz/anchor"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
description = "Solana Sealevel eDSL"
|
||||
|
||||
[features]
|
||||
allow-missing-optionals = ["anchor-derive-accounts/allow-missing-optionals"]
|
||||
init-if-needed = ["anchor-derive-accounts/init-if-needed"]
|
||||
derive = []
|
||||
default = []
|
||||
|
@ -18,27 +19,23 @@ anchor-debug = [
|
|||
"anchor-attribute-constant/anchor-debug",
|
||||
"anchor-attribute-error/anchor-debug",
|
||||
"anchor-attribute-event/anchor-debug",
|
||||
"anchor-attribute-interface/anchor-debug",
|
||||
"anchor-attribute-program/anchor-debug",
|
||||
"anchor-attribute-program/anchor-debug",
|
||||
"anchor-attribute-state/anchor-debug",
|
||||
"anchor-derive-accounts/anchor-debug"
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
anchor-attribute-access-control = { path = "./attribute/access-control", version = "0.25.0" }
|
||||
anchor-attribute-account = { path = "./attribute/account", version = "0.25.0" }
|
||||
anchor-attribute-constant = { path = "./attribute/constant", version = "0.25.0" }
|
||||
anchor-attribute-error = { path = "./attribute/error", version = "0.25.0" }
|
||||
anchor-attribute-program = { path = "./attribute/program", version = "0.25.0" }
|
||||
anchor-attribute-state = { path = "./attribute/state", version = "0.25.0" }
|
||||
anchor-attribute-interface = { path = "./attribute/interface", version = "0.25.0" }
|
||||
anchor-attribute-event = { path = "./attribute/event", version = "0.25.0" }
|
||||
anchor-derive-accounts = { path = "./derive/accounts", version = "0.25.0" }
|
||||
anchor-attribute-access-control = { path = "./attribute/access-control", version = "0.26.0" }
|
||||
anchor-attribute-account = { path = "./attribute/account", version = "0.26.0" }
|
||||
anchor-attribute-constant = { path = "./attribute/constant", version = "0.26.0" }
|
||||
anchor-attribute-error = { path = "./attribute/error", version = "0.26.0" }
|
||||
anchor-attribute-program = { path = "./attribute/program", version = "0.26.0" }
|
||||
anchor-attribute-event = { path = "./attribute/event", version = "0.26.0" }
|
||||
anchor-derive-accounts = { path = "./derive/accounts", version = "0.26.0" }
|
||||
arrayref = "0.3.6"
|
||||
base64 = "0.13.0"
|
||||
borsh = "0.9"
|
||||
bytemuck = "1.4.0"
|
||||
solana-program = "~1.10.29"
|
||||
solana-program = "1.13.5"
|
||||
thiserror = "1.0.20"
|
||||
bincode = "1.3.3"
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
[package]
|
||||
name = "anchor-attribute-access-control"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/coral-xyz/anchor"
|
||||
license = "Apache-2.0"
|
||||
description = "Anchor attribute macro for instruction access control"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
@ -19,5 +19,5 @@ proc-macro2 = "1.0"
|
|||
quote = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anyhow = "1.0.32"
|
||||
anchor-syn = { path = "../../syn", version = "0.25.0" }
|
||||
anchor-syn = { path = "../../syn", version = "0.26.0" }
|
||||
regex = "1.0"
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
[package]
|
||||
name = "anchor-attribute-account"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/coral-xyz/anchor"
|
||||
license = "Apache-2.0"
|
||||
description = "Anchor attribute macro for defining an account"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
@ -19,6 +19,6 @@ proc-macro2 = "1.0"
|
|||
quote = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anyhow = "1.0.32"
|
||||
anchor-syn = { path = "../../syn", version = "0.25.0", features = ["hash"] }
|
||||
anchor-syn = { path = "../../syn", version = "0.26.0", features = ["hash"] }
|
||||
rustversion = "1.0.3"
|
||||
bs58 = "0.4.0"
|
|
@ -214,10 +214,10 @@ fn parse_pubkey(
|
|||
) -> Result<proc_macro2::TokenStream> {
|
||||
let id_vec = bs58::decode(id_literal.value())
|
||||
.into_vec()
|
||||
.map_err(|_| syn::Error::new_spanned(&id_literal, "failed to decode base58 string"))?;
|
||||
.map_err(|_| syn::Error::new_spanned(id_literal, "failed to decode base58 string"))?;
|
||||
let id_array = <[u8; 32]>::try_from(<&[u8]>::clone(&&id_vec[..])).map_err(|_| {
|
||||
syn::Error::new_spanned(
|
||||
&id_literal,
|
||||
id_literal,
|
||||
format!("pubkey array is not 32 bytes long: len={}", id_vec.len()),
|
||||
)
|
||||
})?;
|
||||
|
|
|
@ -139,9 +139,7 @@ pub fn account(
|
|||
|
||||
#[automatically_derived]
|
||||
impl #impl_gen anchor_lang::Discriminator for #account_name #type_gen #where_clause {
|
||||
fn discriminator() -> [u8; 8] {
|
||||
#discriminator
|
||||
}
|
||||
const DISCRIMINATOR: [u8; 8] = #discriminator;
|
||||
}
|
||||
|
||||
// This trait is useful for clients deserializing accounts.
|
||||
|
@ -211,9 +209,7 @@ pub fn account(
|
|||
|
||||
#[automatically_derived]
|
||||
impl #impl_gen anchor_lang::Discriminator for #account_name #type_gen #where_clause {
|
||||
fn discriminator() -> [u8; 8] {
|
||||
#discriminator
|
||||
}
|
||||
const DISCRIMINATOR: [u8; 8] = #discriminator;
|
||||
}
|
||||
|
||||
#owner_impl
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
[package]
|
||||
name = "anchor-attribute-constant"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/coral-xyz/anchor"
|
||||
license = "Apache-2.0"
|
||||
description = "Anchor attribute macro for creating constant types"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
@ -17,4 +17,4 @@ anchor-debug = ["anchor-syn/anchor-debug"]
|
|||
[dependencies]
|
||||
proc-macro2 = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anchor-syn = { path = "../../syn", version = "0.25.0" }
|
||||
anchor-syn = { path = "../../syn", version = "0.26.0" }
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
[package]
|
||||
name = "anchor-attribute-error"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/coral-xyz/anchor"
|
||||
license = "Apache-2.0"
|
||||
description = "Anchor attribute macro for creating error types"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
@ -18,4 +18,4 @@ anchor-debug = ["anchor-syn/anchor-debug"]
|
|||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anchor-syn = { path = "../../syn", version = "0.25.0" }
|
||||
anchor-syn = { path = "../../syn", version = "0.26.0" }
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
[package]
|
||||
name = "anchor-attribute-event"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/coral-xyz/anchor"
|
||||
license = "Apache-2.0"
|
||||
description = "Anchor attribute macro for defining an event"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
@ -19,4 +19,4 @@ proc-macro2 = "1.0"
|
|||
quote = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anyhow = "1.0.32"
|
||||
anchor-syn = { path = "../../syn", version = "0.25.0", features = ["hash"] }
|
||||
anchor-syn = { path = "../../syn", version = "0.26.0", features = ["hash"] }
|
||||
|
|
|
@ -40,9 +40,7 @@ pub fn event(
|
|||
}
|
||||
|
||||
impl anchor_lang::Discriminator for #event_name {
|
||||
fn discriminator() -> [u8; 8] {
|
||||
#discriminator
|
||||
}
|
||||
const DISCRIMINATOR: [u8; 8] = #discriminator;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
[package]
|
||||
name = "anchor-attribute-interface"
|
||||
version = "0.25.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/coral-xyz/anchor"
|
||||
license = "Apache-2.0"
|
||||
description = "Attribute for defining a program interface trait"
|
||||
rust-version = "1.56"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
anchor-debug = ["anchor-syn/anchor-debug"]
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anyhow = "1.0.32"
|
||||
anchor-syn = { path = "../../syn", version = "0.25.0" }
|
||||
heck = "0.3.2"
|
|
@ -1,243 +0,0 @@
|
|||
extern crate proc_macro;
|
||||
|
||||
use anchor_syn::parser;
|
||||
use heck::SnakeCase;
|
||||
use quote::quote;
|
||||
use syn::parse_macro_input;
|
||||
|
||||
/// The `#[interface]` attribute allows one to define an external program
|
||||
/// dependency, without having any knowledge about the program, other than
|
||||
/// the fact that it implements the given trait.
|
||||
///
|
||||
/// Additionally, the attribute generates a client that can be used to perform
|
||||
/// CPI to these external dependencies.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// In the following example, we have a counter program, where the count
|
||||
/// can only be set if the configured external program authorizes it.
|
||||
///
|
||||
/// ## Defining an `#[interface]`
|
||||
///
|
||||
/// First we define the program that depends on an external interface.
|
||||
///
|
||||
/// ```ignore
|
||||
/// use anchor_lang::prelude::*;
|
||||
///
|
||||
/// #[interface]
|
||||
/// pub trait Auth<'info, T: Accounts<'info>> {
|
||||
/// fn is_authorized(ctx: Context<T>, current: u64, new: u64) -> anchor_lang::Result<()>;
|
||||
/// }
|
||||
///
|
||||
/// #[program]
|
||||
/// pub mod counter {
|
||||
/// use super::*;
|
||||
///
|
||||
/// #[state]
|
||||
/// pub struct Counter {
|
||||
/// pub count: u64,
|
||||
/// pub auth_program: Pubkey,
|
||||
/// }
|
||||
///
|
||||
/// impl Counter {
|
||||
/// pub fn new(_ctx: Context<Empty>, auth_program: Pubkey) -> Result<Self> {
|
||||
/// Ok(Self {
|
||||
/// count: 0,
|
||||
/// auth_program,
|
||||
/// })
|
||||
/// }
|
||||
///
|
||||
/// #[access_control(SetCount::accounts(&self, &ctx))]
|
||||
/// pub fn set_count(&mut self, ctx: Context<SetCount>, new_count: u64) -> Result<()> {
|
||||
/// // Ask the auth program if we should approve the transaction.
|
||||
/// let cpi_program = ctx.accounts.auth_program.clone();
|
||||
/// let cpi_ctx = CpiContext::new(cpi_program, Empty {});
|
||||
///
|
||||
/// // This is the client generated by the `#[interface]` attribute.
|
||||
/// auth::is_authorized(cpi_ctx, self.count, new_count)?;
|
||||
///
|
||||
/// // Approved, so update.
|
||||
/// self.count = new_count;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Accounts)]
|
||||
/// pub struct Empty {}
|
||||
///
|
||||
/// #[derive(Accounts)]
|
||||
/// pub struct SetCount<'info> {
|
||||
/// auth_program: AccountInfo<'info>,
|
||||
/// }
|
||||
///
|
||||
/// impl<'info> SetCount<'info> {
|
||||
/// pub fn accounts(counter: &Counter, ctx: &Context<SetCount>) -> Result<()> {
|
||||
/// if ctx.accounts.auth_program.key != &counter.auth_program {
|
||||
/// return Err(error!(ErrorCode::InvalidAuthProgram));
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[error_code]
|
||||
/// pub enum ErrorCode {
|
||||
/// #[msg("Invalid auth program.")]
|
||||
/// InvalidAuthProgram,
|
||||
/// }
|
||||
///```
|
||||
///
|
||||
/// ## Defining an implementation
|
||||
///
|
||||
/// Now we define the program that implements the interface, which the above
|
||||
/// program will call.
|
||||
///
|
||||
/// ```ignore
|
||||
/// use anchor_lang::prelude::*;
|
||||
/// use counter::Auth;
|
||||
///
|
||||
/// #[program]
|
||||
/// pub mod counter_auth {
|
||||
/// use super::*;
|
||||
///
|
||||
/// #[state]
|
||||
/// pub struct CounterAuth;
|
||||
///
|
||||
/// impl<'info> Auth<'info, Empty> for CounterAuth {
|
||||
/// fn is_authorized(_ctx: Context<Empty>, current: u64, new: u64) -> Result<()> {
|
||||
/// if current % 2 == 0 {
|
||||
/// if new % 2 == 0 {
|
||||
/// return Err(ProgramError::Custom(50).into()); // Arbitrary error code.
|
||||
/// }
|
||||
/// } else {
|
||||
/// if new % 2 == 1 {
|
||||
/// return Err(ProgramError::Custom(60).into()); // Arbitrary error code.
|
||||
/// }
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// #[derive(Accounts)]
|
||||
/// pub struct Empty {}
|
||||
/// ```
|
||||
///
|
||||
/// # Returning Values Across CPI
|
||||
///
|
||||
/// The caller above uses a `Result` to act as a boolean. However, in order
|
||||
/// for this feature to be maximally useful, we need a way to return values from
|
||||
/// interfaces. For now, one can do this by writing to a shared account, e.g.,
|
||||
/// with the SPL's [Shared Memory Program](https://github.com/solana-labs/solana-program-library/tree/master/shared-memory).
|
||||
/// In the future, Anchor will add the ability to return values across CPI
|
||||
/// without having to worry about the details of shared memory accounts.
|
||||
#[proc_macro_attribute]
|
||||
pub fn interface(
|
||||
_args: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let item_trait = parse_macro_input!(input as syn::ItemTrait);
|
||||
|
||||
let trait_name = item_trait.ident.to_string();
|
||||
let mod_name: proc_macro2::TokenStream = item_trait
|
||||
.ident
|
||||
.to_string()
|
||||
.to_snake_case()
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
let methods: Vec<proc_macro2::TokenStream> = item_trait
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|trait_item: &syn::TraitItem| match trait_item {
|
||||
syn::TraitItem::Method(m) => Some(m),
|
||||
_ => None,
|
||||
})
|
||||
.map(|method: &syn::TraitItemMethod| {
|
||||
let method_name = &method.sig.ident;
|
||||
let args: Vec<&syn::PatType> = method
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg: &syn::FnArg| match arg {
|
||||
syn::FnArg::Typed(pat_ty) => Some(pat_ty),
|
||||
// TODO: just map this to None once we allow this feature.
|
||||
_ => panic!("Invalid syntax. No self allowed."),
|
||||
})
|
||||
.filter(|pat_ty| {
|
||||
let mut ty = parser::tts_to_string(&pat_ty.ty);
|
||||
ty.retain(|s| !s.is_whitespace());
|
||||
!ty.starts_with("Context<")
|
||||
})
|
||||
.collect();
|
||||
let args_no_tys: Vec<&Box<syn::Pat>> = args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
&arg.pat
|
||||
})
|
||||
.collect();
|
||||
let args_struct = {
|
||||
if args.is_empty() {
|
||||
quote! {
|
||||
use anchor_lang::prelude::borsh;
|
||||
#[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)]
|
||||
struct Args;
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
use anchor_lang::prelude::borsh;
|
||||
#[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)]
|
||||
struct Args {
|
||||
#(#args),*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let sighash_arr = anchor_syn::codegen::program::common::sighash(&trait_name, &method_name.to_string());
|
||||
let sighash_tts: proc_macro2::TokenStream =
|
||||
format!("{:?}", sighash_arr).parse().unwrap();
|
||||
quote! {
|
||||
pub fn #method_name<'a,'b, 'c, 'info, T: anchor_lang::Accounts<'info> + anchor_lang::ToAccountMetas + anchor_lang::ToAccountInfos<'info>>(
|
||||
ctx: anchor_lang::context::CpiContext<'a, 'b, 'c, 'info, T>,
|
||||
#(#args),*
|
||||
) -> anchor_lang::Result<()> {
|
||||
#args_struct
|
||||
|
||||
let ix = {
|
||||
let ix = Args {
|
||||
#(#args_no_tys),*
|
||||
};
|
||||
let mut ix_data = anchor_lang::AnchorSerialize::try_to_vec(&ix)
|
||||
.map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotSerialize)?;
|
||||
let mut data = #sighash_tts.to_vec();
|
||||
data.append(&mut ix_data);
|
||||
let accounts = ctx.to_account_metas(None);
|
||||
anchor_lang::solana_program::instruction::Instruction {
|
||||
program_id: *ctx.program.key,
|
||||
accounts,
|
||||
data,
|
||||
}
|
||||
};
|
||||
let mut acc_infos = ctx.to_account_infos();
|
||||
acc_infos.push(ctx.program.clone());
|
||||
anchor_lang::solana_program::program::invoke_signed(
|
||||
&ix,
|
||||
&acc_infos,
|
||||
ctx.signer_seeds,
|
||||
).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
proc_macro::TokenStream::from(quote! {
|
||||
#item_trait
|
||||
|
||||
/// Anchor generated module for invoking programs implementing an
|
||||
/// `#[interface]` via CPI.
|
||||
mod #mod_name {
|
||||
use super::*;
|
||||
#(#methods)*
|
||||
}
|
||||
})
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
[package]
|
||||
name = "anchor-attribute-program"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/coral-xyz/anchor"
|
||||
license = "Apache-2.0"
|
||||
description = "Anchor attribute macro for defining a program"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
@ -19,4 +19,4 @@ proc-macro2 = "1.0"
|
|||
quote = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anyhow = "1.0.32"
|
||||
anchor-syn = { path = "../../syn", version = "0.25.0" }
|
||||
anchor-syn = { path = "../../syn", version = "0.26.0" }
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
[package]
|
||||
name = "anchor-attribute-state"
|
||||
version = "0.25.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/coral-xyz/anchor"
|
||||
license = "Apache-2.0"
|
||||
description = "Attribute for defining a program state struct"
|
||||
rust-version = "1.56"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
anchor-debug = ["anchor-syn/anchor-debug"]
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anyhow = "1.0.32"
|
||||
anchor-syn = { path = "../../syn", version = "0.25.0" }
|
|
@ -1,92 +0,0 @@
|
|||
extern crate proc_macro;
|
||||
|
||||
use quote::quote;
|
||||
use syn::parse_macro_input;
|
||||
|
||||
/// The `#[state]` attribute defines the program's state struct, i.e., the
|
||||
/// program's global account singleton giving the program the illusion of state.
|
||||
///
|
||||
/// To allocate space into the account on initialization, pass in the account
|
||||
/// size into the macro, e.g., `#[state(SIZE)]`. Otherwise, the size of the
|
||||
/// account returned by the struct's `new` constructor will determine the
|
||||
/// account size. When determining a size, make sure to reserve enough space
|
||||
/// for the 8 byte account discriminator prepended to the account. That is,
|
||||
/// always use 8 extra bytes.
|
||||
///
|
||||
/// # Zero Copy Deserialization
|
||||
///
|
||||
/// Similar to the `#[account]` attribute one can enable zero copy
|
||||
/// deserialization by using the `zero_copy` argument:
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[state(zero_copy)]
|
||||
/// ```
|
||||
///
|
||||
/// For more, see the [`account`](./attr.account.html) attribute.
|
||||
#[deprecated(
|
||||
since = "0.14.0",
|
||||
note = "#[state] will be removed in a future version. Use a PDA with static seeds instead"
|
||||
)]
|
||||
#[proc_macro_attribute]
|
||||
pub fn state(
|
||||
args: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let item_struct = parse_macro_input!(input as syn::ItemStruct);
|
||||
let struct_ident = &item_struct.ident;
|
||||
let is_zero_copy = args.to_string() == "zero_copy";
|
||||
|
||||
let size_override = {
|
||||
if args.is_empty() {
|
||||
// No size override given. The account size is whatever is given
|
||||
// as the initialized value. Use the default implementation.
|
||||
quote! {
|
||||
impl anchor_lang::__private::AccountSize for #struct_ident {
|
||||
fn size(&self) -> anchor_lang::Result<u64> {
|
||||
Ok(8 + self
|
||||
.try_to_vec()
|
||||
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotSerialize)?
|
||||
.len() as u64)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if is_zero_copy {
|
||||
quote! {
|
||||
impl anchor_lang::__private::AccountSize for #struct_ident {
|
||||
fn size(&self) -> anchor_lang::Result<u64> {
|
||||
let len = anchor_lang::__private::bytemuck::bytes_of(self).len() as u64;
|
||||
Ok(8 + len)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let size = proc_macro2::TokenStream::from(args);
|
||||
// Size override given to the macro. Use it.
|
||||
quote! {
|
||||
impl anchor_lang::__private::AccountSize for #struct_ident {
|
||||
fn size(&self) -> anchor_lang::Result<u64> {
|
||||
Ok(#size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let attribute = match is_zero_copy {
|
||||
false => quote! {
|
||||
#[cfg_attr(feature = "anchor-deprecated-state", account)]
|
||||
#[cfg_attr(not(feature = "anchor-deprecated-state"), account("state"))]
|
||||
},
|
||||
true => quote! {
|
||||
#[cfg_attr(feature = "anchor-deprecated-state", account(zero_copy))]
|
||||
#[cfg_attr(not(feature = "anchor-deprecated-state"), account("state", zero_copy))]
|
||||
},
|
||||
};
|
||||
|
||||
proc_macro::TokenStream::from(quote! {
|
||||
#attribute
|
||||
#item_struct
|
||||
|
||||
#size_override
|
||||
})
|
||||
}
|
|
@ -1,17 +1,18 @@
|
|||
[package]
|
||||
name = "anchor-derive-accounts"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/coral-xyz/anchor"
|
||||
license = "Apache-2.0"
|
||||
description = "Anchor Derive macro for accounts"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
allow-missing-optionals = ["anchor-syn/allow-missing-optionals"]
|
||||
init-if-needed = ["anchor-syn/init-if-needed"]
|
||||
default = []
|
||||
anchor-debug = ["anchor-syn/anchor-debug"]
|
||||
|
@ -21,4 +22,4 @@ proc-macro2 = "1.0"
|
|||
quote = "1.0"
|
||||
syn = { version = "1.0.60", features = ["full"] }
|
||||
anyhow = "1.0.32"
|
||||
anchor-syn = { path = "../../syn", version = "0.25.0" }
|
||||
anchor-syn = { path = "../../syn", version = "0.26.0" }
|
||||
|
|
|
@ -336,8 +336,8 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AccountsEx
|
|||
for Account<'info, T>
|
||||
{
|
||||
fn exit(&self, program_id: &Pubkey) -> Result<()> {
|
||||
// Only persist if the owner is the current program.
|
||||
if &T::owner() == program_id {
|
||||
// Only persist if the owner is the current program and the account is not closed.
|
||||
if &T::owner() == program_id && !crate::common::is_closed(&self.info) {
|
||||
let info = self.to_account_info();
|
||||
let mut data = info.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
|
@ -348,13 +348,6 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AccountsEx
|
|||
}
|
||||
}
|
||||
|
||||
/// This function is for INTERNAL USE ONLY.
|
||||
/// Do NOT use this function in a program.
|
||||
/// Manual closing of `Account<'info, T>` types is NOT supported.
|
||||
///
|
||||
/// Details: Using `close` with `Account<'info, T>` is not safe because
|
||||
/// it requires the `mut` constraint but for that type the constraint
|
||||
/// overwrites the "closed account" discriminator at the end of the instruction.
|
||||
impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AccountsClose<'info>
|
||||
for Account<'info, T>
|
||||
{
|
||||
|
@ -404,7 +397,7 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Owner + Clone> Deref for Acc
|
|||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&(*self).account
|
||||
&(self).account
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -235,22 +235,18 @@ impl<'info, T: ZeroCopy + Owner> Accounts<'info> for AccountLoader<'info, T> {
|
|||
|
||||
impl<'info, T: ZeroCopy + Owner> AccountsExit<'info> for AccountLoader<'info, T> {
|
||||
// The account *cannot* be loaded when this is called.
|
||||
fn exit(&self, _program_id: &Pubkey) -> Result<()> {
|
||||
let mut data = self.acc_info.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
let mut writer = BpfWriter::new(dst);
|
||||
writer.write_all(&T::discriminator()).unwrap();
|
||||
fn exit(&self, program_id: &Pubkey) -> Result<()> {
|
||||
// Only persist if the owner is the current program and the account is not closed.
|
||||
if &T::owner() == program_id && !crate::common::is_closed(&self.acc_info) {
|
||||
let mut data = self.acc_info.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
let mut writer = BpfWriter::new(dst);
|
||||
writer.write_all(&T::discriminator()).unwrap();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// This function is for INTERNAL USE ONLY.
|
||||
/// Do NOT use this function in a program.
|
||||
/// Manual closing of `AccountLoader<'info, T>` types is NOT supported.
|
||||
///
|
||||
/// Details: Using `close` with `AccountLoader<'info, T>` is not safe because
|
||||
/// it requires the `mut` constraint but for that type the constraint
|
||||
/// overwrites the "closed account" discriminator at the end of the instruction.
|
||||
impl<'info, T: ZeroCopy + Owner> AccountsClose<'info> for AccountLoader<'info, T> {
|
||||
fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> {
|
||||
crate::common::close(self.to_account_info(), sol_destination)
|
||||
|
|
|
@ -1,149 +0,0 @@
|
|||
use crate::error::ErrorCode;
|
||||
#[allow(deprecated)]
|
||||
use crate::{accounts::state::ProgramState, context::CpiStateContext};
|
||||
use crate::{
|
||||
AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Result, ToAccountInfos,
|
||||
ToAccountMetas,
|
||||
};
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::instruction::AccountMeta;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
/// Boxed container for the program state singleton, used when the state
|
||||
/// is for a program not currently executing.
|
||||
#[derive(Clone)]
|
||||
#[deprecated]
|
||||
pub struct CpiState<'info, T: AccountSerialize + AccountDeserialize + Clone> {
|
||||
inner: Box<Inner<'info, T>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Inner<'info, T: AccountSerialize + AccountDeserialize + Clone> {
|
||||
info: AccountInfo<'info>,
|
||||
account: T,
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> CpiState<'info, T> {
|
||||
pub fn new(i: AccountInfo<'info>, account: T) -> CpiState<'info, T> {
|
||||
Self {
|
||||
inner: Box::new(Inner { info: i, account }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserializes the given `info` into a `CpiState`.
|
||||
#[inline(never)]
|
||||
pub fn try_from(info: &AccountInfo<'info>) -> Result<CpiState<'info, T>> {
|
||||
let mut data: &[u8] = &info.try_borrow_data()?;
|
||||
Ok(CpiState::new(info.clone(), T::try_deserialize(&mut data)?))
|
||||
}
|
||||
|
||||
fn seed() -> &'static str {
|
||||
ProgramState::<T>::seed()
|
||||
}
|
||||
|
||||
pub fn address(program_id: &Pubkey) -> Pubkey {
|
||||
let (base, _nonce) = Pubkey::find_program_address(&[], program_id);
|
||||
let seed = Self::seed();
|
||||
let owner = program_id;
|
||||
Pubkey::create_with_seed(&base, seed, owner).unwrap()
|
||||
}
|
||||
|
||||
/// Convenience api for creating a `CpiStateContext`.
|
||||
pub fn context<'a, 'b, 'c, A: Accounts<'info>>(
|
||||
&self,
|
||||
program: AccountInfo<'info>,
|
||||
accounts: A,
|
||||
) -> CpiStateContext<'a, 'b, 'c, 'info, A> {
|
||||
CpiStateContext::new(program, self.inner.info.clone(), accounts)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T> Accounts<'info> for CpiState<'info, T>
|
||||
where
|
||||
T: AccountSerialize + AccountDeserialize + Clone,
|
||||
{
|
||||
#[inline(never)]
|
||||
fn try_accounts(
|
||||
_program_id: &Pubkey,
|
||||
accounts: &mut &[AccountInfo<'info>],
|
||||
_ix_data: &[u8],
|
||||
_bumps: &mut BTreeMap<String, u8>,
|
||||
_reallocs: &mut BTreeSet<Pubkey>,
|
||||
) -> Result<Self> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
let account = &accounts[0];
|
||||
*accounts = &accounts[1..];
|
||||
|
||||
// No owner or address check is done here. One must use the
|
||||
// #[account(state = <account-name>)] constraint.
|
||||
|
||||
CpiState::try_from(account)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountMetas
|
||||
for CpiState<'info, T>
|
||||
{
|
||||
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
|
||||
let is_signer = is_signer.unwrap_or(self.inner.info.is_signer);
|
||||
let meta = match self.inner.info.is_writable {
|
||||
false => AccountMeta::new_readonly(*self.inner.info.key, is_signer),
|
||||
true => AccountMeta::new(*self.inner.info.key, is_signer),
|
||||
};
|
||||
vec![meta]
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'info>
|
||||
for CpiState<'info, T>
|
||||
{
|
||||
fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
|
||||
vec![self.inner.info.clone()]
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef<AccountInfo<'info>>
|
||||
for CpiState<'info, T>
|
||||
{
|
||||
fn as_ref(&self) -> &AccountInfo<'info> {
|
||||
&self.inner.info
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> Deref for CpiState<'info, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&(*self.inner).account
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> DerefMut for CpiState<'info, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut DerefMut::deref_mut(&mut self.inner).account
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsExit<'info>
|
||||
for CpiState<'info, T>
|
||||
{
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> Key for CpiState<'info, T> {
|
||||
fn key(&self) -> Pubkey {
|
||||
*self.inner.info.key
|
||||
}
|
||||
}
|
|
@ -179,21 +179,17 @@ impl<'info, T: ZeroCopy> Accounts<'info> for Loader<'info, T> {
|
|||
impl<'info, T: ZeroCopy> AccountsExit<'info> for Loader<'info, T> {
|
||||
// The account *cannot* be loaded when this is called.
|
||||
fn exit(&self, _program_id: &Pubkey) -> Result<()> {
|
||||
let mut data = self.acc_info.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
let mut writer = BpfWriter::new(dst);
|
||||
writer.write_all(&T::discriminator()).unwrap();
|
||||
// Only persist if the account is not closed.
|
||||
if !crate::common::is_closed(&self.acc_info) {
|
||||
let mut data = self.acc_info.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
let mut writer = BpfWriter::new(dst);
|
||||
writer.write_all(&T::discriminator()).unwrap();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// This function is for INTERNAL USE ONLY.
|
||||
/// Do NOT use this function in a program.
|
||||
/// Manual closing of `Loader<'info, T>` types is NOT supported.
|
||||
///
|
||||
/// Details: Using `close` with `Loader<'info, T>` is not safe because
|
||||
/// it requires the `mut` constraint but for that type the constraint
|
||||
/// overwrites the "closed account" discriminator at the end of the instruction.
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T: ZeroCopy> AccountsClose<'info> for Loader<'info, T> {
|
||||
fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> {
|
||||
|
|
|
@ -9,18 +9,13 @@ pub mod boxed;
|
|||
pub mod cpi_account;
|
||||
#[doc(hidden)]
|
||||
#[allow(deprecated)]
|
||||
pub mod cpi_state;
|
||||
#[doc(hidden)]
|
||||
#[allow(deprecated)]
|
||||
pub mod loader;
|
||||
pub mod option;
|
||||
pub mod program;
|
||||
#[doc(hidden)]
|
||||
#[allow(deprecated)]
|
||||
pub mod program_account;
|
||||
pub mod signer;
|
||||
#[doc(hidden)]
|
||||
#[allow(deprecated)]
|
||||
pub mod state;
|
||||
pub mod system_account;
|
||||
pub mod sysvar;
|
||||
pub mod unchecked_account;
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
//! Option<T> type for optional accounts.
|
||||
//!
|
||||
//! # Example
|
||||
//! ```ignore
|
||||
//! #[derive(Accounts)]
|
||||
//! pub struct Example {
|
||||
//! pub my_acc: Option<Account<'info, MyData>>
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::instruction::AccountMeta;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
|
||||
use crate::{
|
||||
error::ErrorCode, Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas,
|
||||
};
|
||||
|
||||
impl<'info, T: Accounts<'info>> Accounts<'info> for Option<T> {
|
||||
fn try_accounts(
|
||||
program_id: &Pubkey,
|
||||
accounts: &mut &[AccountInfo<'info>],
|
||||
ix_data: &[u8],
|
||||
bumps: &mut BTreeMap<String, u8>,
|
||||
reallocs: &mut BTreeSet<Pubkey>,
|
||||
) -> Result<Self> {
|
||||
if accounts.is_empty() {
|
||||
return if cfg!(feature = "allow-missing-optionals") {
|
||||
// We don't care if accounts is empty (when this feature is active),
|
||||
// so if that's the case we return None. This allows adding optional
|
||||
// accounts at the end of the Accounts struct without causing a breaking
|
||||
// change. This is safe and will error out if a required account is then
|
||||
// added after the optional account and the accounts aren't passed in.
|
||||
Ok(None)
|
||||
} else {
|
||||
// If the feature is inactive (it is off by default), then we error out
|
||||
// like every other Account.
|
||||
Err(ErrorCode::AccountNotEnoughKeys.into())
|
||||
};
|
||||
}
|
||||
|
||||
// If there are enough accounts, it will check the program_id and return
|
||||
// None if it matches, popping the first account off the accounts vec.
|
||||
if accounts[0].key == program_id {
|
||||
*accounts = &accounts[1..];
|
||||
Ok(None)
|
||||
} else {
|
||||
// If the program_id doesn't equal the account key, we default to
|
||||
// the try_accounts implementation for the inner type and then wrap that with
|
||||
// Some. This should handle all possible valid cases.
|
||||
T::try_accounts(program_id, accounts, ix_data, bumps, reallocs).map(Some)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Option<T> {
|
||||
fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
|
||||
self.as_ref()
|
||||
.map_or_else(Vec::new, |account| account.to_account_infos())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToAccountMetas> ToAccountMetas for Option<T> {
|
||||
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
|
||||
self.as_ref()
|
||||
.expect("Cannot run `to_account_metas` on None")
|
||||
.to_account_metas(is_signer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'info, T: AccountsClose<'info>> AccountsClose<'info> for Option<T> {
|
||||
fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> {
|
||||
self.as_ref()
|
||||
.map_or(Ok(()), |t| T::close(t, sol_destination))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'info, T: AccountsExit<'info>> AccountsExit<'info> for Option<T> {
|
||||
fn exit(&self, program_id: &Pubkey) -> Result<()> {
|
||||
self.as_ref().map_or(Ok(()), |t| t.exit(program_id))
|
||||
}
|
||||
}
|
|
@ -99,22 +99,18 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsExit<'info
|
|||
for ProgramAccount<'info, T>
|
||||
{
|
||||
fn exit(&self, _program_id: &Pubkey) -> Result<()> {
|
||||
let info = self.to_account_info();
|
||||
let mut data = info.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
let mut writer = BpfWriter::new(dst);
|
||||
self.inner.account.try_serialize(&mut writer)?;
|
||||
// Only persist if the account is not closed.
|
||||
if !crate::common::is_closed(&self.inner.info) {
|
||||
let info = self.to_account_info();
|
||||
let mut data = info.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
let mut writer = BpfWriter::new(dst);
|
||||
self.inner.account.try_serialize(&mut writer)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// This function is for INTERNAL USE ONLY.
|
||||
/// Do NOT use this function in a program.
|
||||
/// Manual closing of `ProgramAccount<'info, T>` types is NOT supported.
|
||||
///
|
||||
/// Details: Using `close` with `ProgramAccount<'info, T>` is not safe because
|
||||
/// it requires the `mut` constraint but for that type the constraint
|
||||
/// overwrites the "closed account" discriminator at the end of the instruction.
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsClose<'info>
|
||||
for ProgramAccount<'info, T>
|
||||
|
@ -161,7 +157,7 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Clone> Deref for ProgramAcco
|
|||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&(*self.inner).account
|
||||
&(self.inner).account
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,171 +0,0 @@
|
|||
#[allow(deprecated)]
|
||||
use crate::accounts::cpi_account::CpiAccount;
|
||||
use crate::bpf_writer::BpfWriter;
|
||||
use crate::error::{Error, ErrorCode};
|
||||
use crate::{
|
||||
AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Result, ToAccountInfo,
|
||||
ToAccountInfos, ToAccountMetas,
|
||||
};
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::instruction::AccountMeta;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
pub const PROGRAM_STATE_SEED: &str = "unversioned";
|
||||
|
||||
/// Boxed container for the program state singleton.
|
||||
#[derive(Clone)]
|
||||
#[deprecated]
|
||||
pub struct ProgramState<'info, T: AccountSerialize + AccountDeserialize + Clone> {
|
||||
inner: Box<Inner<'info, T>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Inner<'info, T: AccountSerialize + AccountDeserialize + Clone> {
|
||||
info: AccountInfo<'info>,
|
||||
account: T,
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
|
||||
impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramState<'a, T> {
|
||||
fn new(info: AccountInfo<'a>, account: T) -> ProgramState<'a, T> {
|
||||
Self {
|
||||
inner: Box::new(Inner { info, account }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserializes the given `info` into a `ProgramState`.
|
||||
#[inline(never)]
|
||||
pub fn try_from(program_id: &Pubkey, info: &AccountInfo<'a>) -> Result<ProgramState<'a, T>> {
|
||||
if info.owner != program_id {
|
||||
return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram)
|
||||
.with_pubkeys((*info.owner, *program_id)));
|
||||
}
|
||||
if info.key != &Self::address(program_id) {
|
||||
solana_program::msg!("Invalid state address");
|
||||
return Err(ErrorCode::StateInvalidAddress.into());
|
||||
}
|
||||
let mut data: &[u8] = &info.try_borrow_data()?;
|
||||
Ok(ProgramState::new(
|
||||
info.clone(),
|
||||
T::try_deserialize(&mut data)?,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn seed() -> &'static str {
|
||||
PROGRAM_STATE_SEED
|
||||
}
|
||||
|
||||
pub fn address(program_id: &Pubkey) -> Pubkey {
|
||||
address(program_id)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T> Accounts<'info> for ProgramState<'info, T>
|
||||
where
|
||||
T: AccountSerialize + AccountDeserialize + Clone,
|
||||
{
|
||||
#[inline(never)]
|
||||
fn try_accounts(
|
||||
program_id: &Pubkey,
|
||||
accounts: &mut &[AccountInfo<'info>],
|
||||
_ix_data: &[u8],
|
||||
_bumps: &mut BTreeMap<String, u8>,
|
||||
_reallocs: &mut BTreeSet<Pubkey>,
|
||||
) -> Result<Self> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
let account = &accounts[0];
|
||||
*accounts = &accounts[1..];
|
||||
ProgramState::try_from(program_id, account)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountMetas
|
||||
for ProgramState<'info, T>
|
||||
{
|
||||
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
|
||||
let is_signer = is_signer.unwrap_or(self.inner.info.is_signer);
|
||||
let meta = match self.inner.info.is_writable {
|
||||
false => AccountMeta::new_readonly(*self.inner.info.key, is_signer),
|
||||
true => AccountMeta::new(*self.inner.info.key, is_signer),
|
||||
};
|
||||
vec![meta]
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'info>
|
||||
for ProgramState<'info, T>
|
||||
{
|
||||
fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
|
||||
vec![self.inner.info.clone()]
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef<AccountInfo<'info>>
|
||||
for ProgramState<'info, T>
|
||||
{
|
||||
fn as_ref(&self) -> &AccountInfo<'info> {
|
||||
&self.inner.info
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'a, T: AccountSerialize + AccountDeserialize + Clone> Deref for ProgramState<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&(*self.inner).account
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'a, T: AccountSerialize + AccountDeserialize + Clone> DerefMut for ProgramState<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut DerefMut::deref_mut(&mut self.inner).account
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T> From<CpiAccount<'info, T>> for ProgramState<'info, T>
|
||||
where
|
||||
T: AccountSerialize + AccountDeserialize + Clone,
|
||||
{
|
||||
fn from(a: CpiAccount<'info, T>) -> Self {
|
||||
Self::new(a.to_account_info(), Deref::deref(&a).clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsExit<'info>
|
||||
for ProgramState<'info, T>
|
||||
{
|
||||
fn exit(&self, _program_id: &Pubkey) -> Result<()> {
|
||||
let info = self.to_account_info();
|
||||
let mut data = info.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
let mut writer = BpfWriter::new(dst);
|
||||
self.inner.account.try_serialize(&mut writer)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn address(program_id: &Pubkey) -> Pubkey {
|
||||
let (base, _nonce) = Pubkey::find_program_address(&[], program_id);
|
||||
let seed = PROGRAM_STATE_SEED;
|
||||
let owner = program_id;
|
||||
Pubkey::create_with_seed(&base, seed, owner).unwrap()
|
||||
}
|
||||
|
||||
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> Key for ProgramState<'info, T> {
|
||||
fn key(&self) -> Pubkey {
|
||||
*self.inner.info.key
|
||||
}
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
use crate::bpf_writer::BpfWriter;
|
||||
use crate::error::ErrorCode;
|
||||
use crate::prelude::error;
|
||||
use crate::prelude::{Id, System};
|
||||
use crate::Result;
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use std::io::Write;
|
||||
use solana_program::system_program;
|
||||
|
||||
pub fn close<'info>(info: AccountInfo<'info>, sol_destination: AccountInfo<'info>) -> Result<()> {
|
||||
// Transfer tokens from the account to the sol_destination.
|
||||
|
@ -12,11 +10,10 @@ pub fn close<'info>(info: AccountInfo<'info>, sol_destination: AccountInfo<'info
|
|||
dest_starting_lamports.checked_add(info.lamports()).unwrap();
|
||||
**info.lamports.borrow_mut() = 0;
|
||||
|
||||
// Mark the account discriminator as closed.
|
||||
let mut data = info.try_borrow_mut_data()?;
|
||||
let dst: &mut [u8] = &mut data;
|
||||
let mut writer = BpfWriter::new(dst);
|
||||
writer
|
||||
.write_all(&crate::__private::CLOSED_ACCOUNT_DISCRIMINATOR)
|
||||
.map_err(|_| error!(ErrorCode::AccountDidNotSerialize))
|
||||
info.assign(&system_program::ID);
|
||||
info.realloc(0, false).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn is_closed(info: &AccountInfo) -> bool {
|
||||
info.owner == &System::id() && info.data_is_empty()
|
||||
}
|
||||
|
|
|
@ -241,85 +241,3 @@ impl<'info, T: ToAccountInfos<'info> + ToAccountMetas> ToAccountMetas
|
|||
metas
|
||||
}
|
||||
}
|
||||
|
||||
/// Context specifying non-argument inputs for cross-program-invocations
|
||||
/// targeted at program state instructions.
|
||||
#[doc(hidden)]
|
||||
#[deprecated]
|
||||
pub struct CpiStateContext<'a, 'b, 'c, 'info, T: Accounts<'info>> {
|
||||
state: AccountInfo<'info>,
|
||||
cpi_ctx: CpiContext<'a, 'b, 'c, 'info, T>,
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'a, 'b, 'c, 'info, T: Accounts<'info>> CpiStateContext<'a, 'b, 'c, 'info, T> {
|
||||
pub fn new(program: AccountInfo<'info>, state: AccountInfo<'info>, accounts: T) -> Self {
|
||||
Self {
|
||||
state,
|
||||
cpi_ctx: CpiContext {
|
||||
accounts,
|
||||
program,
|
||||
signer_seeds: &[],
|
||||
remaining_accounts: Vec::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_signer(
|
||||
program: AccountInfo<'info>,
|
||||
state: AccountInfo<'info>,
|
||||
accounts: T,
|
||||
signer_seeds: &'a [&'b [&'c [u8]]],
|
||||
) -> Self {
|
||||
Self {
|
||||
state,
|
||||
cpi_ctx: CpiContext {
|
||||
accounts,
|
||||
program,
|
||||
signer_seeds,
|
||||
remaining_accounts: Vec::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_signer(mut self, signer_seeds: &'a [&'b [&'c [u8]]]) -> Self {
|
||||
self.cpi_ctx = self.cpi_ctx.with_signer(signer_seeds);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn program(&self) -> &AccountInfo<'info> {
|
||||
&self.cpi_ctx.program
|
||||
}
|
||||
|
||||
pub fn signer_seeds(&self) -> &[&[&[u8]]] {
|
||||
self.cpi_ctx.signer_seeds
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'a, 'b, 'c, 'info, T: Accounts<'info>> ToAccountMetas
|
||||
for CpiStateContext<'a, 'b, 'c, 'info, T>
|
||||
{
|
||||
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
|
||||
// State account is always first for state instructions.
|
||||
let mut metas = vec![match self.state.is_writable {
|
||||
false => AccountMeta::new_readonly(*self.state.key, false),
|
||||
true => AccountMeta::new(*self.state.key, false),
|
||||
}];
|
||||
metas.append(&mut self.cpi_ctx.accounts.to_account_metas(is_signer));
|
||||
metas
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'a, 'b, 'c, 'info, T: Accounts<'info>> ToAccountInfos<'info>
|
||||
for CpiStateContext<'a, 'b, 'c, 'info, T>
|
||||
{
|
||||
fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
|
||||
let mut infos = self.cpi_ctx.accounts.to_account_infos();
|
||||
infos.push(self.state.clone());
|
||||
infos.push(self.cpi_ctx.program.clone());
|
||||
infos
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ pub const ERROR_CODE_OFFSET: u32 = 6000;
|
|||
/// - >= 1000 IDL error codes
|
||||
/// - >= 2000 constraint error codes
|
||||
/// - >= 3000 account error codes
|
||||
/// - = 4000 state error code
|
||||
/// - >= 4100 misc error codes
|
||||
/// - = 5000 deprecated error code
|
||||
///
|
||||
|
@ -67,8 +66,8 @@ pub enum ErrorCode {
|
|||
/// 2007 - An executable constraint was violated
|
||||
#[msg("An executable constraint was violated")]
|
||||
ConstraintExecutable,
|
||||
/// 2008 - A state constraint was violated
|
||||
#[msg("A state constraint was violated")]
|
||||
/// 2008 - Deprecated Error, feel free to replace with something else
|
||||
#[msg("Deprecated Error, feel free to replace with something else")]
|
||||
ConstraintState,
|
||||
/// 2009 - An associated constraint was violated
|
||||
#[msg("An associated constraint was violated")]
|
||||
|
@ -105,6 +104,9 @@ pub enum ErrorCode {
|
|||
/// 2019 - A space constraint was violated
|
||||
#[msg("A space constraint was violated")]
|
||||
ConstraintSpace,
|
||||
/// 2020 - A required account for the constraint is None
|
||||
#[msg("A required account for the constraint is None")]
|
||||
ConstraintAccountIsNone,
|
||||
|
||||
// Require
|
||||
/// 2500 - A require expression was violated
|
||||
|
@ -185,15 +187,13 @@ pub enum ErrorCode {
|
|||
#[msg("The account was duplicated for more than one reallocation")]
|
||||
AccountDuplicateReallocs,
|
||||
|
||||
// State.
|
||||
/// 4000 - The given state account does not have the correct address
|
||||
#[msg("The given state account does not have the correct address")]
|
||||
StateInvalidAddress = 4000,
|
||||
|
||||
// Miscellaneous
|
||||
/// 4100 - The declared program id does not match actual program id
|
||||
#[msg("The declared program id does not match the actual program id")]
|
||||
DeclaredProgramIdMismatch = 4100,
|
||||
/// 4101 - You cannot/should not initialize the payer account as a program account
|
||||
#[msg("You cannot/should not initialize the payer account as a program account")]
|
||||
TryingToInitPayerAsProgramAccount = 4101,
|
||||
|
||||
// Deprecated
|
||||
/// 5000 - The API being used is deprecated and should no longer be used
|
||||
|
|
|
@ -49,9 +49,7 @@ pub use anchor_attribute_account::{account, declare_id, zero_copy};
|
|||
pub use anchor_attribute_constant::constant;
|
||||
pub use anchor_attribute_error::*;
|
||||
pub use anchor_attribute_event::{emit, event};
|
||||
pub use anchor_attribute_interface::interface;
|
||||
pub use anchor_attribute_program::program;
|
||||
pub use anchor_attribute_state::state;
|
||||
pub use anchor_derive_accounts::Accounts;
|
||||
/// Borsh is the default serialization format for instructions and accounts.
|
||||
pub use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSerialize};
|
||||
|
@ -182,8 +180,12 @@ pub trait ZeroCopy: Discriminator + Copy + Clone + Zeroable + Pod {}
|
|||
/// `Sha256(<namespace>:<method_name>)[..8] || BorshSerialize(args)`.
|
||||
/// `args` is a borsh serialized struct of named fields for each argument given
|
||||
/// to an instruction.
|
||||
pub trait InstructionData: AnchorSerialize {
|
||||
fn data(&self) -> Vec<u8>;
|
||||
pub trait InstructionData: Discriminator + AnchorSerialize {
|
||||
fn data(&self) -> Vec<u8> {
|
||||
let mut d = Self::discriminator().to_vec();
|
||||
d.append(&mut self.try_to_vec().expect("Should always serialize"));
|
||||
d
|
||||
}
|
||||
}
|
||||
|
||||
/// An event that can be emitted via a Solana log. See [`emit!`](crate::prelude::emit) for an example.
|
||||
|
@ -201,7 +203,10 @@ pub trait EventData: AnchorSerialize + Discriminator {
|
|||
|
||||
/// 8 byte unique identifier for a type.
|
||||
pub trait Discriminator {
|
||||
fn discriminator() -> [u8; 8];
|
||||
const DISCRIMINATOR: [u8; 8];
|
||||
fn discriminator() -> [u8; 8] {
|
||||
Self::DISCRIMINATOR
|
||||
}
|
||||
}
|
||||
|
||||
/// Bump seed for program derived addresses.
|
||||
|
@ -238,12 +243,12 @@ pub mod prelude {
|
|||
accounts::account_loader::AccountLoader, accounts::program::Program,
|
||||
accounts::signer::Signer, accounts::system_account::SystemAccount,
|
||||
accounts::sysvar::Sysvar, accounts::unchecked_account::UncheckedAccount, constant,
|
||||
context::Context, context::CpiContext, declare_id, emit, err, error, event, interface,
|
||||
program, require, require_eq, require_gt, require_gte, require_keys_eq, require_keys_neq,
|
||||
require_neq, solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, source, state,
|
||||
context::Context, context::CpiContext, declare_id, emit, err, error, event, program,
|
||||
require, require_eq, require_gt, require_gte, require_keys_eq, require_keys_neq,
|
||||
require_neq, solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, source,
|
||||
system_program::System, zero_copy, AccountDeserialize, AccountSerialize, Accounts,
|
||||
AccountsExit, AnchorDeserialize, AnchorSerialize, Id, Key, Owner, ProgramData, Result,
|
||||
ToAccountInfo, ToAccountInfos, ToAccountMetas,
|
||||
AccountsClose, AccountsExit, AnchorDeserialize, AnchorSerialize, Id, Key, Owner,
|
||||
ProgramData, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas,
|
||||
};
|
||||
pub use anchor_attribute_error::*;
|
||||
pub use borsh;
|
||||
|
@ -268,7 +273,6 @@ pub mod prelude {
|
|||
/// Internal module used by macros and unstable apis.
|
||||
#[doc(hidden)]
|
||||
pub mod __private {
|
||||
use super::Result;
|
||||
/// The discriminator anchor uses to mark an account as closed.
|
||||
pub const CLOSED_ACCOUNT_DISCRIMINATOR: [u8; 8] = [255, 255, 255, 255, 255, 255, 255, 255];
|
||||
|
||||
|
@ -284,17 +288,6 @@ pub mod __private {
|
|||
|
||||
use solana_program::pubkey::Pubkey;
|
||||
|
||||
pub mod state {
|
||||
pub use crate::accounts::state::*;
|
||||
}
|
||||
|
||||
// Calculates the size of an account, which may be larger than the deserialized
|
||||
// data in it. This trait is currently only used for `#[state]` accounts.
|
||||
#[doc(hidden)]
|
||||
pub trait AccountSize {
|
||||
fn size(&self) -> Result<u64>;
|
||||
}
|
||||
|
||||
// Very experimental trait.
|
||||
#[doc(hidden)]
|
||||
pub trait ZeroCopyAccessor<Ty> {
|
||||
|
@ -311,9 +304,6 @@ pub mod __private {
|
|||
input.to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use crate::accounts::state::PROGRAM_STATE_SEED;
|
||||
}
|
||||
|
||||
/// Ensures a condition is true, otherwise returns with the given error.
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
[package]
|
||||
name = "anchor-syn"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
authors = ["Serum Foundation <foundation@projectserum.com>"]
|
||||
repository = "https://github.com/coral-xyz/anchor"
|
||||
license = "Apache-2.0"
|
||||
description = "Anchor syntax parsing and code generation tools"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.59"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
allow-missing-optionals = []
|
||||
init-if-needed = []
|
||||
idl = []
|
||||
hash = []
|
||||
|
|
|
@ -61,9 +61,16 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
} else {
|
||||
quote!()
|
||||
};
|
||||
quote! {
|
||||
#docs
|
||||
pub #name: anchor_lang::solana_program::pubkey::Pubkey
|
||||
if f.is_optional {
|
||||
quote! {
|
||||
#docs
|
||||
pub #name: Option<anchor_lang::solana_program::pubkey::Pubkey>
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#docs
|
||||
pub #name: anchor_lang::solana_program::pubkey::Pubkey
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -93,8 +100,18 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
true => quote! { anchor_lang::solana_program::instruction::AccountMeta::new },
|
||||
};
|
||||
let name = &f.ident;
|
||||
quote! {
|
||||
account_metas.push(#meta(self.#name, #is_signer));
|
||||
if f.is_optional {
|
||||
quote! {
|
||||
if let Some(#name) = &self.#name {
|
||||
account_metas.push(#meta(*#name, #is_signer));
|
||||
} else {
|
||||
account_metas.push(anchor_lang::solana_program::instruction::AccountMeta::new_readonly(crate::ID, false));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
account_metas.push(#meta(self.#name, #is_signer));
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -62,9 +62,16 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
} else {
|
||||
quote!()
|
||||
};
|
||||
quote! {
|
||||
#docs
|
||||
pub #name: anchor_lang::solana_program::account_info::AccountInfo<'info>
|
||||
if f.is_optional {
|
||||
quote! {
|
||||
#docs
|
||||
pub #name: Option<anchor_lang::solana_program::account_info::AccountInfo<'info>>
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#docs
|
||||
pub #name: anchor_lang::solana_program::account_info::AccountInfo<'info>
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -94,8 +101,18 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
true => quote! { anchor_lang::solana_program::instruction::AccountMeta::new },
|
||||
};
|
||||
let name = &f.ident;
|
||||
quote! {
|
||||
account_metas.push(#meta(anchor_lang::Key::key(&self.#name), #is_signer));
|
||||
if f.is_optional {
|
||||
quote! {
|
||||
if let Some(#name) = &self.#name {
|
||||
account_metas.push(#meta(anchor_lang::Key::key(#name), #is_signer));
|
||||
} else {
|
||||
account_metas.push(anchor_lang::solana_program::instruction::AccountMeta::new_readonly(crate::ID, false));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
account_metas.push(#meta(anchor_lang::Key::key(&self.#name), #is_signer));
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -104,18 +121,10 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
let account_struct_infos: Vec<proc_macro2::TokenStream> = accs
|
||||
.fields
|
||||
.iter()
|
||||
.map(|f: &AccountField| match f {
|
||||
AccountField::CompositeField(s) => {
|
||||
let name = &s.ident;
|
||||
quote! {
|
||||
account_infos.extend(anchor_lang::ToAccountInfos::to_account_infos(&self.#name));
|
||||
}
|
||||
}
|
||||
AccountField::Field(f) => {
|
||||
let name = &f.ident;
|
||||
quote! {
|
||||
account_infos.push(anchor_lang::ToAccountInfo::to_account_info(&self.#name));
|
||||
}
|
||||
.map(|f: &AccountField| {
|
||||
let name = &f.ident();
|
||||
quote! {
|
||||
account_infos.extend(anchor_lang::ToAccountInfos::to_account_infos(&self.#name));
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use crate::*;
|
||||
use proc_macro2_diagnostics::SpanDiagnosticExt;
|
||||
use quote::quote;
|
||||
use std::collections::HashSet;
|
||||
use syn::Expr;
|
||||
|
||||
pub fn generate(f: &Field) -> proc_macro2::TokenStream {
|
||||
use crate::*;
|
||||
|
||||
pub fn generate(f: &Field, accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
||||
let constraints = linearize(&f.constraints);
|
||||
|
||||
let rent = constraints
|
||||
|
@ -14,12 +16,41 @@ pub fn generate(f: &Field) -> proc_macro2::TokenStream {
|
|||
|
||||
let checks: Vec<proc_macro2::TokenStream> = constraints
|
||||
.iter()
|
||||
.map(|c| generate_constraint(f, c))
|
||||
.map(|c| generate_constraint(f, c, accs))
|
||||
.collect();
|
||||
|
||||
let mut all_checks = quote! {#(#checks)*};
|
||||
|
||||
// If the field is optional we do all the inner checks as if the account
|
||||
// wasn't optional. If the account is init we also need to return an Option
|
||||
// by wrapping the resulting value with Some or returning None if it doesn't exist.
|
||||
if f.is_optional && !constraints.is_empty() {
|
||||
let ident = &f.ident;
|
||||
let ty_decl = f.ty_decl(false);
|
||||
all_checks = match &constraints[0] {
|
||||
Constraint::Init(_) | Constraint::Zeroed(_) => {
|
||||
quote! {
|
||||
let #ident: #ty_decl = if let Some(#ident) = #ident {
|
||||
#all_checks
|
||||
Some(#ident)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
quote! {
|
||||
if let Some(#ident) = &#ident {
|
||||
#all_checks
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
quote! {
|
||||
#rent
|
||||
#(#checks)*
|
||||
#all_checks
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +84,6 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
|
|||
rent_exempt,
|
||||
seeds,
|
||||
executable,
|
||||
state,
|
||||
close,
|
||||
address,
|
||||
associated_token,
|
||||
|
@ -97,9 +127,6 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
|
|||
if let Some(c) = executable {
|
||||
constraints.push(Constraint::Executable(c));
|
||||
}
|
||||
if let Some(c) = state {
|
||||
constraints.push(Constraint::State(c));
|
||||
}
|
||||
if let Some(c) = close {
|
||||
constraints.push(Constraint::Close(c));
|
||||
}
|
||||
|
@ -115,12 +142,16 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
|
|||
constraints
|
||||
}
|
||||
|
||||
fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream {
|
||||
fn generate_constraint(
|
||||
f: &Field,
|
||||
c: &Constraint,
|
||||
accs: &AccountsStruct,
|
||||
) -> proc_macro2::TokenStream {
|
||||
match c {
|
||||
Constraint::Init(c) => generate_constraint_init(f, c),
|
||||
Constraint::Init(c) => generate_constraint_init(f, c, accs),
|
||||
Constraint::Zeroed(c) => generate_constraint_zeroed(f, c),
|
||||
Constraint::Mut(c) => generate_constraint_mut(f, c),
|
||||
Constraint::HasOne(c) => generate_constraint_has_one(f, c),
|
||||
Constraint::HasOne(c) => generate_constraint_has_one(f, c, accs),
|
||||
Constraint::Signer(c) => generate_constraint_signer(f, c),
|
||||
Constraint::Literal(c) => generate_constraint_literal(&f.ident, c),
|
||||
Constraint::Raw(c) => generate_constraint_raw(&f.ident, c),
|
||||
|
@ -128,13 +159,12 @@ fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream {
|
|||
Constraint::RentExempt(c) => generate_constraint_rent_exempt(f, c),
|
||||
Constraint::Seeds(c) => generate_constraint_seeds(f, c),
|
||||
Constraint::Executable(c) => generate_constraint_executable(f, c),
|
||||
Constraint::State(c) => generate_constraint_state(f, c),
|
||||
Constraint::Close(c) => generate_constraint_close(f, c),
|
||||
Constraint::Close(c) => generate_constraint_close(f, c, accs),
|
||||
Constraint::Address(c) => generate_constraint_address(f, c),
|
||||
Constraint::AssociatedToken(c) => generate_constraint_associated_token(f, c),
|
||||
Constraint::TokenAccount(c) => generate_constraint_token_account(f, c),
|
||||
Constraint::Mint(c) => generate_constraint_mint(f, c),
|
||||
Constraint::Realloc(c) => generate_constraint_realloc(f, c),
|
||||
Constraint::AssociatedToken(c) => generate_constraint_associated_token(f, c, accs),
|
||||
Constraint::TokenAccount(c) => generate_constraint_token_account(f, c, accs),
|
||||
Constraint::Mint(c) => generate_constraint_mint(f, c, accs),
|
||||
Constraint::Realloc(c) => generate_constraint_realloc(f, c, accs),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,14 +196,18 @@ fn generate_constraint_address(f: &Field, c: &ConstraintAddress) -> proc_macro2:
|
|||
}
|
||||
}
|
||||
|
||||
pub fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream {
|
||||
generate_constraint_init_group(f, c)
|
||||
pub fn generate_constraint_init(
|
||||
f: &Field,
|
||||
c: &ConstraintInitGroup,
|
||||
accs: &AccountsStruct,
|
||||
) -> proc_macro2::TokenStream {
|
||||
generate_constraint_init_group(f, c, accs)
|
||||
}
|
||||
|
||||
pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macro2::TokenStream {
|
||||
let field = &f.ident;
|
||||
let name_str = field.to_string();
|
||||
let ty_decl = f.ty_decl();
|
||||
let ty_decl = f.ty_decl(true);
|
||||
let from_account_info = f.from_account_info(None, false);
|
||||
quote! {
|
||||
let #field: #ty_decl = {
|
||||
|
@ -189,13 +223,22 @@ pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macr
|
|||
}
|
||||
}
|
||||
|
||||
pub fn generate_constraint_close(f: &Field, c: &ConstraintClose) -> proc_macro2::TokenStream {
|
||||
pub fn generate_constraint_close(
|
||||
f: &Field,
|
||||
c: &ConstraintClose,
|
||||
accs: &AccountsStruct,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let field = &f.ident;
|
||||
let name_str = field.to_string();
|
||||
let target = &c.sol_dest;
|
||||
let target_optional_check =
|
||||
OptionalCheckScope::new_with_field(accs, field).generate_check(target);
|
||||
quote! {
|
||||
if #field.key() == #target.key() {
|
||||
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintClose).with_account_name(#name_str));
|
||||
{
|
||||
#target_optional_check
|
||||
if #field.key() == #target.key() {
|
||||
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintClose).with_account_name(#name_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -210,8 +253,12 @@ pub fn generate_constraint_mut(f: &Field, c: &ConstraintMut) -> proc_macro2::Tok
|
|||
}
|
||||
}
|
||||
|
||||
pub fn generate_constraint_has_one(f: &Field, c: &ConstraintHasOne) -> proc_macro2::TokenStream {
|
||||
let target = c.join_target.clone();
|
||||
pub fn generate_constraint_has_one(
|
||||
f: &Field,
|
||||
c: &ConstraintHasOne,
|
||||
accs: &AccountsStruct,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let target = &c.join_target;
|
||||
let ident = &f.ident;
|
||||
let field = match &f.ty {
|
||||
Ty::Loader(_) => quote! {#ident.load()?},
|
||||
|
@ -224,8 +271,12 @@ pub fn generate_constraint_has_one(f: &Field, c: &ConstraintHasOne) -> proc_macr
|
|||
quote! { ConstraintHasOne },
|
||||
&Some(&(quote! { my_key }, quote! { target_key })),
|
||||
);
|
||||
let target_optional_check =
|
||||
OptionalCheckScope::new_with_field(accs, &field).generate_check(target);
|
||||
|
||||
quote! {
|
||||
{
|
||||
#target_optional_check
|
||||
let my_key = #field.#target;
|
||||
let target_key = #target.key();
|
||||
if my_key != target_key {
|
||||
|
@ -325,13 +376,22 @@ pub fn generate_constraint_rent_exempt(
|
|||
}
|
||||
}
|
||||
|
||||
fn generate_constraint_realloc(f: &Field, c: &ConstraintReallocGroup) -> proc_macro2::TokenStream {
|
||||
fn generate_constraint_realloc(
|
||||
f: &Field,
|
||||
c: &ConstraintReallocGroup,
|
||||
accs: &AccountsStruct,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let field = &f.ident;
|
||||
let account_name = field.to_string();
|
||||
let new_space = &c.space;
|
||||
let payer = &c.payer;
|
||||
let zero = &c.zero;
|
||||
|
||||
let mut optional_check_scope = OptionalCheckScope::new_with_field(accs, field);
|
||||
let payer_optional_check = optional_check_scope.generate_check(payer);
|
||||
let system_program_optional_check =
|
||||
optional_check_scope.generate_check(quote! {system_program});
|
||||
|
||||
quote! {
|
||||
// Blocks duplicate account reallocs in a single instruction to prevent accidental account overwrites
|
||||
// and to ensure the calculation of the change in bytes is based on account size at program entry
|
||||
|
@ -349,7 +409,9 @@ fn generate_constraint_realloc(f: &Field, c: &ConstraintReallocGroup) -> proc_ma
|
|||
.unwrap();
|
||||
|
||||
if __delta_space != 0 {
|
||||
#payer_optional_check
|
||||
if __delta_space > 0 {
|
||||
#system_program_optional_check
|
||||
if ::std::convert::TryInto::<usize>::try_into(__delta_space).unwrap() > anchor_lang::solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE {
|
||||
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::AccountReallocExceedsLimit).with_account_name(#account_name));
|
||||
}
|
||||
|
@ -378,10 +440,14 @@ fn generate_constraint_realloc(f: &Field, c: &ConstraintReallocGroup) -> proc_ma
|
|||
}
|
||||
}
|
||||
|
||||
fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream {
|
||||
fn generate_constraint_init_group(
|
||||
f: &Field,
|
||||
c: &ConstraintInitGroup,
|
||||
accs: &AccountsStruct,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let field = &f.ident;
|
||||
let name_str = f.ident.to_string();
|
||||
let ty_decl = f.ty_decl();
|
||||
let ty_decl = f.ty_decl(true);
|
||||
let if_needed = if c.if_needed {
|
||||
quote! {true}
|
||||
} else {
|
||||
|
@ -389,13 +455,7 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
|
|||
};
|
||||
let space = &c.space;
|
||||
|
||||
// Payer for rent exemption.
|
||||
let payer = {
|
||||
let p = &c.payer;
|
||||
quote! {
|
||||
let payer = #p.to_account_info();
|
||||
}
|
||||
};
|
||||
let payer = &c.payer;
|
||||
|
||||
// Convert from account info to account context wrapper type.
|
||||
let from_account_info = f.from_account_info(Some(&c.kind), true);
|
||||
|
@ -417,6 +477,36 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
|
|||
quote! { #seeds, }
|
||||
});
|
||||
|
||||
let validate_pda = {
|
||||
// If the bump is provided with init *and target*, then force it to be the
|
||||
// canonical bump.
|
||||
//
|
||||
// Note that for `#[account(init, seeds)]`, find_program_address has already
|
||||
// been run in the init constraint find_pda variable.
|
||||
if c.bump.is_some() {
|
||||
let b = c.bump.as_ref().unwrap();
|
||||
quote! {
|
||||
if #field.key() != __pda_address {
|
||||
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#field.key(), __pda_address)));
|
||||
}
|
||||
if __bump != #b {
|
||||
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_values((__bump, #b)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Init seeds but no bump. We already used the canonical to create bump so
|
||||
// just check the address.
|
||||
//
|
||||
// Note that for `#[account(init, seeds)]`, find_program_address has already
|
||||
// been run in the init constraint find_pda variable.
|
||||
quote! {
|
||||
if #field.key() != __pda_address {
|
||||
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#field.key(), __pda_address)));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
quote! {
|
||||
let (__pda_address, __bump) = Pubkey::find_program_address(
|
||||
|
@ -424,6 +514,7 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
|
|||
program_id,
|
||||
);
|
||||
__bumps.insert(#name_str.to_string(), __bump);
|
||||
#validate_pda
|
||||
},
|
||||
quote! {
|
||||
&[
|
||||
|
@ -435,36 +526,63 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
|
|||
}
|
||||
};
|
||||
|
||||
// Optional check idents
|
||||
let system_program = "e! {system_program};
|
||||
let token_program = "e! {token_program};
|
||||
let associated_token_program = "e! {associated_token_program};
|
||||
let rent = "e! {rent};
|
||||
|
||||
let mut check_scope = OptionalCheckScope::new_with_field(accs, field);
|
||||
match &c.kind {
|
||||
InitKind::Token { owner, mint } => {
|
||||
let owner_optional_check = check_scope.generate_check(owner);
|
||||
let mint_optional_check = check_scope.generate_check(mint);
|
||||
|
||||
let system_program_optional_check = check_scope.generate_check(system_program);
|
||||
let token_program_optional_check = check_scope.generate_check(token_program);
|
||||
let rent_optional_check = check_scope.generate_check(rent);
|
||||
|
||||
let optional_checks = quote! {
|
||||
#system_program_optional_check
|
||||
#token_program_optional_check
|
||||
#rent_optional_check
|
||||
#owner_optional_check
|
||||
#mint_optional_check
|
||||
};
|
||||
|
||||
let payer_optional_check = check_scope.generate_check(payer);
|
||||
|
||||
let create_account = generate_create_account(
|
||||
field,
|
||||
quote! {anchor_spl::token::TokenAccount::LEN},
|
||||
quote! {&token_program.key()},
|
||||
quote! {#payer},
|
||||
seeds_with_bump,
|
||||
);
|
||||
|
||||
quote! {
|
||||
// Define the bump and pda variable.
|
||||
#find_pda
|
||||
|
||||
let #field: #ty_decl = {
|
||||
// Checks that all the required accounts for this operation are present.
|
||||
#optional_checks
|
||||
|
||||
if !#if_needed || AsRef::<AccountInfo>::as_ref(&#field).owner == &anchor_lang::solana_program::system_program::ID {
|
||||
// Define payer variable.
|
||||
#payer
|
||||
#payer_optional_check
|
||||
|
||||
// Create the account with the system program.
|
||||
#create_account
|
||||
|
||||
// Initialize the token account.
|
||||
let cpi_program = token_program.to_account_info();
|
||||
let accounts = anchor_spl::token::InitializeAccount {
|
||||
let accounts = anchor_spl::token::InitializeAccount3 {
|
||||
account: #field.to_account_info(),
|
||||
mint: #mint.to_account_info(),
|
||||
authority: #owner.to_account_info(),
|
||||
rent: rent.to_account_info(),
|
||||
};
|
||||
let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, accounts);
|
||||
anchor_spl::token::initialize_account(cpi_ctx)?;
|
||||
anchor_spl::token::initialize_account3(cpi_ctx)?;
|
||||
}
|
||||
|
||||
let pa: #ty_decl = #from_account_info_unchecked;
|
||||
|
@ -481,23 +599,45 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
|
|||
}
|
||||
}
|
||||
InitKind::AssociatedToken { owner, mint } => {
|
||||
let owner_optional_check = check_scope.generate_check(owner);
|
||||
let mint_optional_check = check_scope.generate_check(mint);
|
||||
|
||||
let system_program_optional_check = check_scope.generate_check(system_program);
|
||||
let token_program_optional_check = check_scope.generate_check(token_program);
|
||||
let associated_token_program_optional_check =
|
||||
check_scope.generate_check(associated_token_program);
|
||||
let rent_optional_check = check_scope.generate_check(rent);
|
||||
|
||||
let optional_checks = quote! {
|
||||
#system_program_optional_check
|
||||
#token_program_optional_check
|
||||
#associated_token_program_optional_check
|
||||
#rent_optional_check
|
||||
#owner_optional_check
|
||||
#mint_optional_check
|
||||
};
|
||||
|
||||
let payer_optional_check = check_scope.generate_check(payer);
|
||||
|
||||
quote! {
|
||||
// Define the bump and pda variable.
|
||||
#find_pda
|
||||
|
||||
let #field: #ty_decl = {
|
||||
// Checks that all the required accounts for this operation are present.
|
||||
#optional_checks
|
||||
|
||||
if !#if_needed || AsRef::<AccountInfo>::as_ref(&#field).owner == &anchor_lang::solana_program::system_program::ID {
|
||||
#payer
|
||||
#payer_optional_check
|
||||
|
||||
let cpi_program = associated_token_program.to_account_info();
|
||||
let cpi_accounts = anchor_spl::associated_token::Create {
|
||||
payer: payer.to_account_info(),
|
||||
payer: #payer.to_account_info(),
|
||||
associated_token: #field.to_account_info(),
|
||||
authority: #owner.to_account_info(),
|
||||
mint: #mint.to_account_info(),
|
||||
system_program: system_program.to_account_info(),
|
||||
token_program: token_program.to_account_info(),
|
||||
rent: rent.to_account_info(),
|
||||
};
|
||||
let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, cpi_accounts);
|
||||
anchor_spl::associated_token::create(cpi_ctx)?;
|
||||
|
@ -524,36 +664,61 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
|
|||
decimals,
|
||||
freeze_authority,
|
||||
} => {
|
||||
let owner_optional_check = check_scope.generate_check(owner);
|
||||
let freeze_authority_optional_check = match freeze_authority {
|
||||
Some(fa) => check_scope.generate_check(fa),
|
||||
None => quote! {},
|
||||
};
|
||||
|
||||
let system_program_optional_check = check_scope.generate_check(system_program);
|
||||
let token_program_optional_check = check_scope.generate_check(token_program);
|
||||
let rent_optional_check = check_scope.generate_check(rent);
|
||||
|
||||
let optional_checks = quote! {
|
||||
#system_program_optional_check
|
||||
#token_program_optional_check
|
||||
#rent_optional_check
|
||||
#owner_optional_check
|
||||
#freeze_authority_optional_check
|
||||
};
|
||||
|
||||
let payer_optional_check = check_scope.generate_check(payer);
|
||||
|
||||
let create_account = generate_create_account(
|
||||
field,
|
||||
quote! {anchor_spl::token::Mint::LEN},
|
||||
quote! {&token_program.key()},
|
||||
quote! {#payer},
|
||||
seeds_with_bump,
|
||||
);
|
||||
|
||||
let freeze_authority = match freeze_authority {
|
||||
Some(fa) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) },
|
||||
None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None },
|
||||
};
|
||||
|
||||
quote! {
|
||||
// Define the bump and pda variable.
|
||||
#find_pda
|
||||
|
||||
let #field: #ty_decl = {
|
||||
// Checks that all the required accounts for this operation are present.
|
||||
#optional_checks
|
||||
|
||||
if !#if_needed || AsRef::<AccountInfo>::as_ref(&#field).owner == &anchor_lang::solana_program::system_program::ID {
|
||||
// Define payer variable.
|
||||
#payer
|
||||
#payer_optional_check
|
||||
|
||||
// Create the account with the system program.
|
||||
#create_account
|
||||
|
||||
// Initialize the mint account.
|
||||
let cpi_program = token_program.to_account_info();
|
||||
let accounts = anchor_spl::token::InitializeMint {
|
||||
let accounts = anchor_spl::token::InitializeMint2 {
|
||||
mint: #field.to_account_info(),
|
||||
rent: rent.to_account_info(),
|
||||
};
|
||||
let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, accounts);
|
||||
anchor_spl::token::initialize_mint(cpi_ctx, #decimals, &#owner.key(), #freeze_authority)?;
|
||||
anchor_spl::token::initialize_mint2(cpi_ctx, #decimals, &#owner.key(), #freeze_authority)?;
|
||||
}
|
||||
let pa: #ty_decl = #from_account_info_unchecked;
|
||||
if #if_needed {
|
||||
|
@ -578,20 +743,45 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
|
|||
// Define the space variable.
|
||||
let space = quote! {let space = #space;};
|
||||
|
||||
let system_program_optional_check = check_scope.generate_check(system_program);
|
||||
|
||||
// Define the owner of the account being created. If not specified,
|
||||
// default to the currently executing program.
|
||||
let owner = match owner {
|
||||
None => quote! {
|
||||
program_id
|
||||
},
|
||||
Some(o) => quote! {
|
||||
&#o
|
||||
},
|
||||
let (owner, owner_optional_check) = match owner {
|
||||
None => (
|
||||
quote! {
|
||||
program_id
|
||||
},
|
||||
quote! {},
|
||||
),
|
||||
|
||||
Some(o) => {
|
||||
// We clone the `check_scope` here to avoid collisions with the
|
||||
// `payer_optional_check`, which is in a separate scope
|
||||
let owner_optional_check = check_scope.clone().generate_check(o);
|
||||
(
|
||||
quote! {
|
||||
&#o
|
||||
},
|
||||
owner_optional_check,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let payer_optional_check = check_scope.generate_check(payer);
|
||||
|
||||
let optional_checks = quote! {
|
||||
#system_program_optional_check
|
||||
};
|
||||
|
||||
// CPI to the system program to create the account.
|
||||
let create_account =
|
||||
generate_create_account(field, quote! {space}, owner.clone(), seeds_with_bump);
|
||||
let create_account = generate_create_account(
|
||||
field,
|
||||
quote! {space},
|
||||
owner.clone(),
|
||||
quote! {#payer},
|
||||
seeds_with_bump,
|
||||
);
|
||||
|
||||
// Put it all together.
|
||||
quote! {
|
||||
|
@ -599,6 +789,9 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
|
|||
#find_pda
|
||||
|
||||
let #field = {
|
||||
// Checks that all the required accounts for this operation are present.
|
||||
#optional_checks
|
||||
|
||||
let actual_field = #field.to_account_info();
|
||||
let actual_owner = actual_field.owner;
|
||||
|
||||
|
@ -608,8 +801,7 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
|
|||
// Create the account. Always do this in the event
|
||||
// if needed is not specified or the system program is the owner.
|
||||
let pa: #ty_decl = if !#if_needed || actual_owner == &anchor_lang::solana_program::system_program::ID {
|
||||
// Define the payer variable.
|
||||
#payer
|
||||
#payer_optional_check
|
||||
|
||||
// CPI to the system program to create.
|
||||
#create_account
|
||||
|
@ -623,6 +815,7 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
|
|||
|
||||
// Assert the account was created correctly.
|
||||
if #if_needed {
|
||||
#owner_optional_check
|
||||
if space != actual_field.data_len() {
|
||||
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSpace).with_account_name(#name_str).with_values((space, actual_field.data_len())));
|
||||
}
|
||||
|
@ -648,59 +841,36 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
|
|||
}
|
||||
|
||||
fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2::TokenStream {
|
||||
let name = &f.ident;
|
||||
let name_str = name.to_string();
|
||||
if c.is_init {
|
||||
// Note that for `#[account(init, seeds)]`, the seed generation and checks is checked in
|
||||
// the init constraint find_pda/validate_pda block, so we don't do anything here and
|
||||
// return nothing!
|
||||
quote! {}
|
||||
} else {
|
||||
let name = &f.ident;
|
||||
let name_str = name.to_string();
|
||||
|
||||
let s = &mut c.seeds.clone();
|
||||
let s = &mut c.seeds.clone();
|
||||
|
||||
let deriving_program_id = c
|
||||
.program_seed
|
||||
.clone()
|
||||
// If they specified a seeds::program to use when deriving the PDA, use it.
|
||||
.map(|program_id| quote! { #program_id.key() })
|
||||
// Otherwise fall back to the current program's program_id.
|
||||
.unwrap_or(quote! { program_id });
|
||||
let deriving_program_id = c
|
||||
.program_seed
|
||||
.clone()
|
||||
// If they specified a seeds::program to use when deriving the PDA, use it.
|
||||
.map(|program_id| quote! { #program_id.key() })
|
||||
// Otherwise fall back to the current program's program_id.
|
||||
.unwrap_or(quote! { program_id });
|
||||
|
||||
// If the seeds came with a trailing comma, we need to chop it off
|
||||
// before we interpolate them below.
|
||||
if let Some(pair) = s.pop() {
|
||||
s.push_value(pair.into_value());
|
||||
}
|
||||
|
||||
// If the bump is provided with init *and target*, then force it to be the
|
||||
// canonical bump.
|
||||
//
|
||||
// Note that for `#[account(init, seeds)]`, find_program_address has already
|
||||
// been run in the init constraint.
|
||||
if c.is_init && c.bump.is_some() {
|
||||
let b = c.bump.as_ref().unwrap();
|
||||
quote! {
|
||||
if #name.key() != __pda_address {
|
||||
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#name.key(), __pda_address)));
|
||||
}
|
||||
if __bump != #b {
|
||||
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_values((__bump, #b)));
|
||||
}
|
||||
// If the seeds came with a trailing comma, we need to chop it off
|
||||
// before we interpolate them below.
|
||||
if let Some(pair) = s.pop() {
|
||||
s.push_value(pair.into_value());
|
||||
}
|
||||
}
|
||||
// Init seeds but no bump. We already used the canonical to create bump so
|
||||
// just check the address.
|
||||
//
|
||||
// Note that for `#[account(init, seeds)]`, find_program_address has already
|
||||
// been run in the init constraint.
|
||||
else if c.is_init {
|
||||
quote! {
|
||||
if #name.key() != __pda_address {
|
||||
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#name.key(), __pda_address)));
|
||||
}
|
||||
}
|
||||
}
|
||||
// No init. So we just check the address.
|
||||
else {
|
||||
|
||||
let maybe_seeds_plus_comma = (!s.is_empty()).then(|| {
|
||||
quote! { #s, }
|
||||
});
|
||||
|
||||
// Not init here, so do all the checks.
|
||||
let define_pda = match c.bump.as_ref() {
|
||||
// Bump target not given. Find it.
|
||||
None => quote! {
|
||||
|
@ -733,13 +903,25 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2
|
|||
fn generate_constraint_associated_token(
|
||||
f: &Field,
|
||||
c: &ConstraintAssociatedToken,
|
||||
accs: &AccountsStruct,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let name = &f.ident;
|
||||
let name_str = name.to_string();
|
||||
let wallet_address = &c.wallet;
|
||||
let spl_token_mint_address = &c.mint;
|
||||
let mut optional_check_scope = OptionalCheckScope::new_with_field(accs, name);
|
||||
let wallet_address_optional_check = optional_check_scope.generate_check(wallet_address);
|
||||
let spl_token_mint_address_optional_check =
|
||||
optional_check_scope.generate_check(spl_token_mint_address);
|
||||
let optional_checks = quote! {
|
||||
#wallet_address_optional_check
|
||||
#spl_token_mint_address_optional_check
|
||||
};
|
||||
|
||||
quote! {
|
||||
{
|
||||
#optional_checks
|
||||
|
||||
let my_owner = #name.owner;
|
||||
let wallet_address = #wallet_address.key();
|
||||
if my_owner != wallet_address {
|
||||
|
@ -757,27 +939,43 @@ fn generate_constraint_associated_token(
|
|||
fn generate_constraint_token_account(
|
||||
f: &Field,
|
||||
c: &ConstraintTokenAccountGroup,
|
||||
accs: &AccountsStruct,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let name = &f.ident;
|
||||
let mut optional_check_scope = OptionalCheckScope::new_with_field(accs, name);
|
||||
let authority_check = match &c.authority {
|
||||
Some(authority) => {
|
||||
quote! { if #name.owner != #authority.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenOwner.into()); } }
|
||||
let authority_optional_check = optional_check_scope.generate_check(authority);
|
||||
quote! {
|
||||
#authority_optional_check
|
||||
if #name.owner != #authority.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenOwner.into()); }
|
||||
}
|
||||
}
|
||||
None => quote! {},
|
||||
};
|
||||
let mint_check = match &c.mint {
|
||||
Some(mint) => {
|
||||
quote! { if #name.mint != #mint.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenMint.into()); } }
|
||||
let mint_optional_check = optional_check_scope.generate_check(mint);
|
||||
quote! {
|
||||
#mint_optional_check
|
||||
if #name.mint != #mint.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenMint.into()); }
|
||||
}
|
||||
}
|
||||
None => quote! {},
|
||||
};
|
||||
quote! {
|
||||
#authority_check
|
||||
#mint_check
|
||||
{
|
||||
#authority_check
|
||||
#mint_check
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_constraint_mint(f: &Field, c: &ConstraintTokenMintGroup) -> proc_macro2::TokenStream {
|
||||
fn generate_constraint_mint(
|
||||
f: &Field,
|
||||
c: &ConstraintTokenMintGroup,
|
||||
accs: &AccountsStruct,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let name = &f.ident;
|
||||
|
||||
let decimal_check = match &c.decimals {
|
||||
|
@ -788,26 +986,77 @@ fn generate_constraint_mint(f: &Field, c: &ConstraintTokenMintGroup) -> proc_mac
|
|||
},
|
||||
None => quote! {},
|
||||
};
|
||||
let mut optional_check_scope = OptionalCheckScope::new_with_field(accs, name);
|
||||
let mint_authority_check = match &c.mint_authority {
|
||||
Some(mint_authority) => quote! {
|
||||
if #name.mint_authority != anchor_lang::solana_program::program_option::COption::Some(anchor_lang::Key::key(&#mint_authority)) {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintMintMintAuthority.into());
|
||||
Some(mint_authority) => {
|
||||
let mint_authority_optional_check = optional_check_scope.generate_check(mint_authority);
|
||||
quote! {
|
||||
#mint_authority_optional_check
|
||||
if #name.mint_authority != anchor_lang::solana_program::program_option::COption::Some(#mint_authority.key()) {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintMintMintAuthority.into());
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
None => quote! {},
|
||||
};
|
||||
let freeze_authority_check = match &c.freeze_authority {
|
||||
Some(freeze_authority) => quote! {
|
||||
if #name.freeze_authority != anchor_lang::solana_program::program_option::COption::Some(anchor_lang::Key::key(&#freeze_authority)) {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority.into());
|
||||
Some(freeze_authority) => {
|
||||
let freeze_authority_optional_check =
|
||||
optional_check_scope.generate_check(freeze_authority);
|
||||
quote! {
|
||||
#freeze_authority_optional_check
|
||||
if #name.freeze_authority != anchor_lang::solana_program::program_option::COption::Some(#freeze_authority.key()) {
|
||||
return Err(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority.into());
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
None => quote! {},
|
||||
};
|
||||
quote! {
|
||||
#decimal_check
|
||||
#mint_authority_check
|
||||
#freeze_authority_check
|
||||
{
|
||||
#decimal_check
|
||||
#mint_authority_check
|
||||
#freeze_authority_check
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OptionalCheckScope<'a> {
|
||||
seen: HashSet<String>,
|
||||
accounts: &'a AccountsStruct,
|
||||
}
|
||||
|
||||
impl<'a> OptionalCheckScope<'a> {
|
||||
pub fn new(accounts: &'a AccountsStruct) -> Self {
|
||||
Self {
|
||||
seen: HashSet::new(),
|
||||
accounts,
|
||||
}
|
||||
}
|
||||
pub fn new_with_field(accounts: &'a AccountsStruct, field: impl ToString) -> Self {
|
||||
let mut check_scope = Self::new(accounts);
|
||||
check_scope.seen.insert(field.to_string());
|
||||
check_scope
|
||||
}
|
||||
pub fn generate_check(&mut self, field: impl ToTokens) -> TokenStream {
|
||||
let field_name = tts_to_string(&field);
|
||||
if self.seen.contains(&field_name) {
|
||||
quote! {}
|
||||
} else {
|
||||
self.seen.insert(field_name.clone());
|
||||
if self.accounts.is_field_optional(&field) {
|
||||
quote! {
|
||||
let #field = if let Some(ref account) = #field {
|
||||
account
|
||||
} else {
|
||||
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintAccountIsNone).with_account_name(#field_name));
|
||||
};
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -816,12 +1065,16 @@ fn generate_constraint_mint(f: &Field, c: &ConstraintTokenMintGroup) -> proc_mac
|
|||
//
|
||||
// `seeds_with_nonce` should be given for creating PDAs. Otherwise it's an
|
||||
// empty stream.
|
||||
pub fn generate_create_account(
|
||||
//
|
||||
// This should only be run within scopes where `system_program` is not Optional
|
||||
fn generate_create_account(
|
||||
field: &Ident,
|
||||
space: proc_macro2::TokenStream,
|
||||
owner: proc_macro2::TokenStream,
|
||||
payer: proc_macro2::TokenStream,
|
||||
seeds_with_nonce: proc_macro2::TokenStream,
|
||||
) -> proc_macro2::TokenStream {
|
||||
// Field, payer, and system program are already validated to not be an Option at this point
|
||||
quote! {
|
||||
// If the account being initialized already has lamports, then
|
||||
// return them all back to the payer so that the account has
|
||||
|
@ -832,12 +1085,13 @@ pub fn generate_create_account(
|
|||
// Create the token account with right amount of lamports and space, and the correct owner.
|
||||
let lamports = __anchor_rent.minimum_balance(#space);
|
||||
let cpi_accounts = anchor_lang::system_program::CreateAccount {
|
||||
from: payer.to_account_info(),
|
||||
from: #payer.to_account_info(),
|
||||
to: #field.to_account_info()
|
||||
};
|
||||
let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts);
|
||||
anchor_lang::system_program::create_account(cpi_context.with_signer(&[#seeds_with_nonce]), lamports, #space as u64, #owner)?;
|
||||
} else {
|
||||
require_keys_neq!(#payer.key(), #field.key(), anchor_lang::error::ErrorCode::TryingToInitPayerAsProgramAccount);
|
||||
// Fund the account for rent exemption.
|
||||
let required_lamports = __anchor_rent
|
||||
.minimum_balance(#space)
|
||||
|
@ -845,7 +1099,7 @@ pub fn generate_create_account(
|
|||
.saturating_sub(__current_lamports);
|
||||
if required_lamports > 0 {
|
||||
let cpi_accounts = anchor_lang::system_program::Transfer {
|
||||
from: payer.to_account_info(),
|
||||
from: #payer.to_account_info(),
|
||||
to: #field.to_account_info(),
|
||||
};
|
||||
let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts);
|
||||
|
@ -873,6 +1127,9 @@ pub fn generate_constraint_executable(
|
|||
) -> proc_macro2::TokenStream {
|
||||
let name = &f.ident;
|
||||
let name_str = name.to_string();
|
||||
|
||||
// because we are only acting on the field, we know it isnt optional at this point
|
||||
// as it was unwrapped in `generate_constraint`
|
||||
quote! {
|
||||
if !#name.to_account_info().executable {
|
||||
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintExecutable).with_account_name(#name_str));
|
||||
|
@ -880,26 +1137,6 @@ pub fn generate_constraint_executable(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn generate_constraint_state(f: &Field, c: &ConstraintState) -> proc_macro2::TokenStream {
|
||||
let program_target = c.program_target.clone();
|
||||
let ident = &f.ident;
|
||||
let name_str = ident.to_string();
|
||||
let account_ty = match &f.ty {
|
||||
Ty::CpiState(ty) => &ty.account_type_path,
|
||||
_ => panic!("Invalid state constraint"),
|
||||
};
|
||||
quote! {
|
||||
// Checks the given state account is the canonical state account for
|
||||
// the target program.
|
||||
if #ident.key() != anchor_lang::accounts::cpi_state::CpiState::<#account_ty>::address(&#program_target.key()) {
|
||||
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintState).with_account_name(#name_str));
|
||||
}
|
||||
if AsRef::<AccountInfo>::as_ref(&#ident).owner != &#program_target.key() {
|
||||
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintState).with_account_name(#name_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_custom_error(
|
||||
account_name: &Ident,
|
||||
custom_error: &Option<Expr>,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::accounts_codegen::constraints::OptionalCheckScope;
|
||||
use crate::codegen::accounts::{generics, ParsedGenerics};
|
||||
use crate::{AccountField, AccountsStruct};
|
||||
use quote::quote;
|
||||
|
@ -29,11 +30,18 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
let name_str = ident.to_string();
|
||||
if f.constraints.is_close() {
|
||||
let close_target = &f.constraints.close.as_ref().unwrap().sol_dest;
|
||||
let close_target_optional_check =
|
||||
OptionalCheckScope::new(accs).generate_check(close_target);
|
||||
|
||||
quote! {
|
||||
anchor_lang::AccountsClose::close(
|
||||
&self.#ident,
|
||||
self.#close_target.to_account_info(),
|
||||
).map_err(|e| e.with_account_name(#name_str))?;
|
||||
{
|
||||
let #close_target = &self.#close_target;
|
||||
#close_target_optional_check
|
||||
anchor_lang::AccountsClose::close(
|
||||
&self.#ident,
|
||||
#close_target.to_account_info(),
|
||||
).map_err(|e| e.with_account_name(#name_str))?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match f.constraints.is_mutable() {
|
||||
|
|
|
@ -16,13 +16,8 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
.fields
|
||||
.iter()
|
||||
.map(|f: &AccountField| {
|
||||
let name = match f {
|
||||
AccountField::CompositeField(s) => &s.ident,
|
||||
AccountField::Field(f) => &f.ident,
|
||||
};
|
||||
quote! {
|
||||
account_infos.extend(self.#name.to_account_infos());
|
||||
}
|
||||
let name = &f.ident();
|
||||
quote! { account_infos.extend(self.#name.to_account_infos()); }
|
||||
})
|
||||
.collect();
|
||||
quote! {
|
||||
|
|
|
@ -9,18 +9,28 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
.fields
|
||||
.iter()
|
||||
.map(|f: &AccountField| {
|
||||
let (name, is_signer) = match f {
|
||||
AccountField::CompositeField(s) => (&s.ident, quote! {None}),
|
||||
let (name, is_signer, is_optional) = match f {
|
||||
AccountField::CompositeField(s) => (&s.ident, quote! {None}, false),
|
||||
AccountField::Field(f) => {
|
||||
let is_signer = match f.constraints.is_signer() {
|
||||
false => quote! {None},
|
||||
true => quote! {Some(true)},
|
||||
};
|
||||
(&f.ident, is_signer)
|
||||
(&f.ident, is_signer, f.is_optional)
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
account_metas.extend(self.#name.to_account_metas(#is_signer));
|
||||
if is_optional {
|
||||
quote! {
|
||||
if let Some(#name) = &self.#name {
|
||||
account_metas.extend(#name.to_account_metas(#is_signer));
|
||||
} else {
|
||||
account_metas.push(AccountMeta::new_readonly(crate::ID, false));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
account_metas.extend(self.#name.to_account_metas(#is_signer));
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
|
|
@ -32,14 +32,38 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
// `init` and `zero` acccounts are special cased as they are
|
||||
// deserialized by constraints. Here, we just take out the
|
||||
// AccountInfo for later use at constraint validation time.
|
||||
if is_init(af) || f.constraints.zeroed.is_some() {
|
||||
if is_init(af) || f.constraints.zeroed.is_some() {
|
||||
let name = &f.ident;
|
||||
quote!{
|
||||
if accounts.is_empty() {
|
||||
return Err(anchor_lang::error::ErrorCode::AccountNotEnoughKeys.into());
|
||||
// Optional accounts have slightly different behavior here and
|
||||
// we can't leverage the try_accounts implementation for zero and init.
|
||||
if f.is_optional {
|
||||
// Thus, this block essentially reimplements the try_accounts
|
||||
// behavior with optional accounts minus the deserialziation.
|
||||
let empty_behavior = if cfg!(feature = "allow-missing-optionals") {
|
||||
quote!{ None }
|
||||
} else {
|
||||
quote!{ return Err(anchor_lang::error::ErrorCode::AccountNotEnoughKeys.into()); }
|
||||
};
|
||||
quote! {
|
||||
let #name = if accounts.is_empty() {
|
||||
#empty_behavior
|
||||
} else if accounts[0].key == program_id {
|
||||
*accounts = &accounts[1..];
|
||||
None
|
||||
} else {
|
||||
let account = &accounts[0];
|
||||
*accounts = &accounts[1..];
|
||||
Some(account)
|
||||
};
|
||||
}
|
||||
} else {
|
||||
quote!{
|
||||
if accounts.is_empty() {
|
||||
return Err(anchor_lang::error::ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
let #name = &accounts[0];
|
||||
*accounts = &accounts[1..];
|
||||
}
|
||||
let #name = &accounts[0];
|
||||
*accounts = &accounts[1..];
|
||||
}
|
||||
} else {
|
||||
let name = f.ident.to_string();
|
||||
|
@ -129,14 +153,14 @@ pub fn generate_constraints(accs: &AccountsStruct) -> proc_macro2::TokenStream {
|
|||
true => Some(f),
|
||||
},
|
||||
})
|
||||
.map(constraints::generate)
|
||||
.map(|f| constraints::generate(f, accs))
|
||||
.collect();
|
||||
|
||||
// Constraint checks for each account fields.
|
||||
let access_checks: Vec<proc_macro2::TokenStream> = non_init_fields
|
||||
.iter()
|
||||
.map(|af: &&AccountField| match af {
|
||||
AccountField::Field(f) => constraints::generate(f),
|
||||
AccountField::Field(f) => constraints::generate(f, accs),
|
||||
AccountField::CompositeField(s) => constraints::generate_composite(s),
|
||||
})
|
||||
.collect();
|
||||
|
|
|
@ -5,30 +5,6 @@ use quote::quote;
|
|||
pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||
let mut accounts = std::collections::HashSet::new();
|
||||
|
||||
// Go through state accounts.
|
||||
if let Some(state) = &program.state {
|
||||
// Ctor.
|
||||
if let Some((_ctor, ctor_accounts)) = &state.ctor_and_anchor {
|
||||
let macro_name = format!(
|
||||
"__client_accounts_{}",
|
||||
ctor_accounts.to_string().to_snake_case()
|
||||
);
|
||||
accounts.insert(macro_name);
|
||||
}
|
||||
// Methods.
|
||||
if let Some((_impl_block, methods)) = &state.impl_block_and_methods {
|
||||
for ix in methods {
|
||||
let anchor_ident = &ix.anchor_ident;
|
||||
// TODO: move to fn and share with accounts.rs.
|
||||
let macro_name = format!(
|
||||
"__client_accounts_{}",
|
||||
anchor_ident.to_string().to_snake_case()
|
||||
);
|
||||
accounts.insert(macro_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Go through instruction accounts.
|
||||
for ix in &program.ixs {
|
||||
let anchor_ident = &ix.anchor_ident;
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
use crate::parser;
|
||||
use crate::{IxArg, State};
|
||||
use crate::IxArg;
|
||||
use heck::CamelCase;
|
||||
use quote::quote;
|
||||
|
||||
// Namespace for calculating state instruction sighash signatures.
|
||||
pub const SIGHASH_STATE_NAMESPACE: &str = "state";
|
||||
|
||||
// Namespace for calculating instruction sighash signatures for any instruction
|
||||
// not affecting program state.
|
||||
pub const SIGHASH_GLOBAL_NAMESPACE: &str = "global";
|
||||
|
@ -22,10 +18,6 @@ pub fn sighash(namespace: &str, name: &str) -> [u8; 8] {
|
|||
sighash
|
||||
}
|
||||
|
||||
pub fn sighash_ctor() -> [u8; 8] {
|
||||
sighash(SIGHASH_STATE_NAMESPACE, "new")
|
||||
}
|
||||
|
||||
pub fn generate_ix_variant(name: String, args: &[IxArg]) -> proc_macro2::TokenStream {
|
||||
let ix_arg_names: Vec<&syn::Ident> = args.iter().map(|arg| &arg.name).collect();
|
||||
let ix_name_camel: proc_macro2::TokenStream = {
|
||||
|
@ -45,39 +37,3 @@ pub fn generate_ix_variant(name: String, args: &[IxArg]) -> proc_macro2::TokenSt
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_ctor_args(state: &State) -> Vec<syn::Pat> {
|
||||
generate_ctor_typed_args(state)
|
||||
.iter()
|
||||
.map(|pat_ty| *pat_ty.pat.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn generate_ctor_typed_args(state: &State) -> Vec<syn::PatType> {
|
||||
state
|
||||
.ctor_and_anchor
|
||||
.as_ref()
|
||||
.map(|(ctor, _anchor_ident)| {
|
||||
ctor.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg: &syn::FnArg| match arg {
|
||||
syn::FnArg::Typed(pat_ty) => {
|
||||
let mut arg_str = parser::tts_to_string(&pat_ty.ty);
|
||||
arg_str.retain(|c| !c.is_whitespace());
|
||||
if arg_str.starts_with("Context<") {
|
||||
return None;
|
||||
}
|
||||
Some(pat_ty.clone())
|
||||
}
|
||||
_ => {
|
||||
if !state.is_zero_copy {
|
||||
panic!("Cannot pass self as parameter")
|
||||
}
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
|
|
@ -1,61 +1,9 @@
|
|||
use crate::codegen::program::common::{generate_ix_variant, sighash, SIGHASH_GLOBAL_NAMESPACE};
|
||||
use crate::Program;
|
||||
use crate::StateIx;
|
||||
use heck::SnakeCase;
|
||||
use quote::{quote, ToTokens};
|
||||
|
||||
pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||
// Generate cpi methods for the state struct.
|
||||
// The Ctor is not exposed via CPI, since it is a one time use function.
|
||||
let state_cpi_methods: Vec<proc_macro2::TokenStream> = program
|
||||
.state
|
||||
.as_ref()
|
||||
.map(|state| {
|
||||
state
|
||||
.impl_block_and_methods
|
||||
.as_ref()
|
||||
.map(|(_, methods)| {
|
||||
methods
|
||||
.iter()
|
||||
.map(|method: &StateIx| {
|
||||
let accounts_ident = &method.anchor_ident;
|
||||
let ix_variant = generate_ix_variant(
|
||||
method.raw_method.sig.ident.to_string(),
|
||||
&method.args,
|
||||
);
|
||||
let method_name = &method.ident;
|
||||
let args: Vec<&syn::PatType> =
|
||||
method.args.iter().map(|arg| &arg.raw_arg).collect();
|
||||
|
||||
quote! {
|
||||
pub fn #method_name<'a, 'b, 'c, 'info>(
|
||||
ctx: anchor_lang::context::CpiStateContext<'a, 'b, 'c, 'info, #accounts_ident<'info>>,
|
||||
#(#args),*
|
||||
) -> anchor_lang::Result<()> {
|
||||
let ix = {
|
||||
let ix = instruction::state::#ix_variant;
|
||||
let data = anchor_lang::InstructionData::data(&ix);
|
||||
let accounts = ctx.to_account_metas(None);
|
||||
anchor_lang::solana_program::instruction::Instruction {
|
||||
program_id: crate::ID,
|
||||
accounts,
|
||||
data,
|
||||
}
|
||||
};
|
||||
let mut acc_infos = ctx.to_account_infos();
|
||||
anchor_lang::solana_program::program::invoke_signed(
|
||||
&ix,
|
||||
&acc_infos,
|
||||
ctx.signer_seeds(),
|
||||
).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_else(Vec::new)
|
||||
})
|
||||
.unwrap_or_else(Vec::new);
|
||||
// Generate cpi methods for global methods.
|
||||
let global_cpi_methods: Vec<proc_macro2::TokenStream> = program
|
||||
.ixs
|
||||
|
@ -123,11 +71,6 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
use super::*;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub mod state {
|
||||
use super::*;
|
||||
|
||||
#(#state_cpi_methods)*
|
||||
}
|
||||
|
||||
pub struct Return<T> {
|
||||
phantom: std::marker::PhantomData<T>
|
||||
|
@ -150,30 +93,6 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
pub fn generate_accounts(program: &Program) -> proc_macro2::TokenStream {
|
||||
let mut accounts = std::collections::HashSet::new();
|
||||
|
||||
// Go through state accounts.
|
||||
if let Some(state) = &program.state {
|
||||
// Ctor.
|
||||
if let Some((_ctor, ctor_accounts)) = &state.ctor_and_anchor {
|
||||
let macro_name = format!(
|
||||
"__cpi_client_accounts_{}",
|
||||
ctor_accounts.to_string().to_snake_case()
|
||||
);
|
||||
accounts.insert(macro_name);
|
||||
}
|
||||
// Methods.
|
||||
if let Some((_impl_block, methods)) = &state.impl_block_and_methods {
|
||||
for ix in methods {
|
||||
let anchor_ident = &ix.anchor_ident;
|
||||
// TODO: move to fn and share with accounts.rs.
|
||||
let macro_name = format!(
|
||||
"__cpi_client_accounts_{}",
|
||||
anchor_ident.to_string().to_snake_case()
|
||||
);
|
||||
accounts.insert(macro_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Go through instruction accounts.
|
||||
for ix in &program.ixs {
|
||||
let anchor_ident = &ix.anchor_ident;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue