ソースを参照

Solana: Allow address and bytes to be used as @seed (#1497)

Allows `address` or `bytesN` to have the @seed annotation on
constructors.

        contract bar {
            @payer(wallet)
constructor(@seed address seed, @seed bytes2 seed2, @bump byte b) {}
        }

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 2 年 前
コミット
4c5fc506f5

+ 1 - 1
docs/targets/solana.rst

@@ -186,7 +186,7 @@ then the seeds and bump have to be provided. There can be multiple seeds, and an
 single bump. If the bump is not provided, then the seeds must not create an
 account that falls on the curve. When placed above the constructor, the ``@seed`` can be a string literal,
 or a hex string with the format ``hex"4142"``. If before an argument, the seed annotation must refer to an argument
-of type ``bytes``. The ``@bump`` must a single byte of type ``bytes1``.
+of type ``bytes``, ``address``, or fixed length byte array of ``bytesN``. The ``@bump`` must a single byte of type ``bytes1``.
 
 .. _value_transfer:
 

+ 1 - 1
integration/solana/UserStats.sol

@@ -10,7 +10,7 @@ contract UserStats {
     @payer(wallet)
     @seed("user-stats")
     @space(250)
-    constructor(@seed bytes user_key, @bump uint8 _bump, string _name, uint16 _level) {
+    constructor(@seed address user_key, @bump uint8 _bump, string _name, uint16 _level) {
         name = _name;
         level = _level;
         bump = _bump;

+ 7 - 7
integration/solana/user_stats.spec.ts

@@ -1,17 +1,17 @@
 // SPDX-License-Identifier: Apache-2.0
 
-import {loadContract} from "./setup";
-import {Keypair, PublicKey} from "@solana/web3.js";
+import { loadContract } from "./setup";
+import { Keypair, PublicKey } from "@solana/web3.js";
 import { utils } from '@coral-xyz/anchor';
 import expect from "expect";
 
-describe('PDA hash table', function() {
+describe('PDA hash table', function () {
     // A PDA (Program derived address) hash table is a way to store values for a provided key
     // on a unique account on chain, resembling a hash table. This is an example for achieving
     // so with Solidity.
 
     it('Table functions', async function test_table() {
-        const {program, payer} = await loadContract("UserStats");
+        const { program, payer } = await loadContract("UserStats");
         // A user's public key will be the key for the hash table in this example.
         const myUser = Keypair.generate();
 
@@ -29,10 +29,10 @@ describe('PDA hash table', function() {
         // We create the account to hold the user's related information. The generated PDA becomes the
         // data account for our contract.
         // If a contract for `userStatsPDA` already exists, this function will fail.
-        await program.methods.new(myUser.publicKey.toBuffer(), bump, "user-one", 25)
+        await program.methods.new(myUser.publicKey, bump, "user-one", 25)
             .accounts({
-                    dataAccount: userStatsPDA,
-                    wallet: payer.publicKey,
+                dataAccount: userStatsPDA,
+                wallet: payer.publicKey,
             })
             .signers([payer])
             .rpc();

+ 1 - 1
src/codegen/cfg.rs

@@ -933,7 +933,7 @@ impl ControlFlowGraph {
                     .collect::<Vec<String>>()
                     .join(", ")
             ),
-            Expression::InternalFunctionCfg { cfg_no } => {
+            Expression::InternalFunctionCfg { cfg_no, .. } => {
                 format!("function {}", contract.cfg[*cfg_no].name)
             }
             Expression::ReturnData { .. } => "(external call return data)".to_string(),

+ 2 - 2
src/codegen/encoding/scale_encoding.rs

@@ -335,7 +335,7 @@ fn encode_compact(
             Instr::WriteBuffer {
                 buf: buffer.clone(),
                 offset: offset.clone(),
-                value: Expression::Cast {
+                value: Expression::Trunc {
                     loc: Codegen,
                     ty: Uint(8),
                     expr: mul.clone().into(),
@@ -371,7 +371,7 @@ fn encode_compact(
             Instr::WriteBuffer {
                 buf: buffer.clone(),
                 offset: offset.clone(),
-                value: Expression::Cast {
+                value: Expression::Trunc {
                     loc: Codegen,
                     ty: Uint(16),
                     expr: mul.into(),

+ 2 - 0
src/codegen/expression.rs

@@ -486,6 +486,7 @@ pub fn expression(
         ast::Expression::InternalFunction {
             function_no,
             signature,
+            ty,
             ..
         } => {
             let function_no = if let Some(signature) = signature {
@@ -495,6 +496,7 @@ pub fn expression(
             };
 
             Expression::InternalFunctionCfg {
+                ty: ty.clone(),
                 cfg_no: ns.contracts[contract_no].all_functions[function_no],
             }
         }

+ 3 - 2
src/codegen/mod.rs

@@ -465,6 +465,7 @@ pub enum Expression {
         expr: Box<Expression>,
     },
     InternalFunctionCfg {
+        ty: Type,
         cfg_no: usize,
     },
     Keccak256 {
@@ -839,7 +840,8 @@ impl RetrieveType for Expression {
             | Expression::AllocDynamicBytes { ty, .. }
             | Expression::BytesCast { ty, .. }
             | Expression::RationalNumberLiteral { ty, .. }
-            | Expression::Subscript { ty, .. } => ty.clone(),
+            | Expression::Subscript { ty, .. }
+            | Expression::InternalFunctionCfg { ty, .. } => ty.clone(),
 
             Expression::BoolLiteral { .. }
             | Expression::MoreEqual { .. }
@@ -859,7 +861,6 @@ impl RetrieveType for Expression {
 
             Expression::AdvancePointer { .. } => Type::BufferPointer,
             Expression::FormatString { .. } => Type::String,
-            Expression::InternalFunctionCfg { .. } => Type::Unreachable,
             Expression::Poison => unreachable!("Expression does not have a type"),
         }
     }

+ 4 - 1
src/codegen/strength_reduce/mod.rs

@@ -562,7 +562,10 @@ fn expression_reduce(expr: &Expression, vars: &Variables, ns: &mut Namespace) ->
 /// Other types (e.g. bytes) is not relevant for strength reduce. Bools are only
 /// tracked so we can following branching after integer compare.
 fn track(ty: &Type) -> bool {
-    matches!(ty, Type::Uint(_) | Type::Int(_) | Type::Bool | Type::Value)
+    matches!(
+        ty,
+        Type::Uint(_) | Type::Int(_) | Type::Bool | Type::Value | Type::UserType(_)
+    )
 }
 
 // A variable can

+ 185 - 71
src/emit/expression.rs

@@ -1815,7 +1815,7 @@ pub(super) fn expression<'a, T: TargetRuntime<'a> + ?Sized>(
                 .into()
         }
         Expression::Builtin { .. } => target.builtin(bin, e, vartab, function, ns),
-        Expression::InternalFunctionCfg { cfg_no } => bin.functions[cfg_no]
+        Expression::InternalFunctionCfg { cfg_no, .. } => bin.functions[cfg_no]
             .as_global_value()
             .as_pointer_value()
             .into(),
@@ -1904,106 +1904,220 @@ fn runtime_cast<'a>(
     val: BasicValueEnum<'a>,
     ns: &Namespace,
 ) -> BasicValueEnum<'a> {
-    if matches!(from, Type::Address(_) | Type::Contract(_))
-        && matches!(to, Type::Address(_) | Type::Contract(_))
-    {
+    match (from, to) {
         // no conversion needed
-        val
-    } else if let Type::Address(_) = to {
-        let llvm_ty = bin.llvm_type(from, ns);
-
-        let src = bin.build_alloca(function, llvm_ty, "dest");
-
-        bin.builder.build_store(src, val.into_int_value());
-
-        let dest = bin.build_alloca(function, bin.address_type(ns), "address");
-
-        let len = bin
-            .context
-            .i32_type()
-            .const_int(ns.address_length as u64, false);
+        (from, to) if from == to => val,
+
+        (Type::Address(_) | Type::Contract(_), Type::Address(_) | Type::Contract(_)) => val,
+        (
+            Type::ExternalFunction { .. } | Type::Struct(StructType::ExternalFunction),
+            Type::ExternalFunction { .. } | Type::Struct(StructType::ExternalFunction),
+        ) => val,
+        (
+            Type::Uint(_)
+            | Type::Int(_)
+            | Type::Value
+            | Type::Bytes(_)
+            | Type::UserType(_)
+            | Type::Enum(_)
+            | Type::FunctionSelector,
+            Type::Uint(_)
+            | Type::Int(_)
+            | Type::Value
+            | Type::Bytes(_)
+            | Type::Enum(_)
+            | Type::UserType(_)
+            | Type::FunctionSelector,
+        ) => {
+            assert_eq!(from.bytes(ns), to.bytes(ns),);
+
+            val
+        }
+        (Type::String | Type::DynamicBytes, Type::String | Type::DynamicBytes) => val,
+        (
+            Type::InternalFunction {
+                params: from_params,
+                returns: from_returns,
+                ..
+            },
+            Type::InternalFunction {
+                params: to_params,
+                returns: to_returns,
+                ..
+            },
+        ) if from_params == to_params && from_returns == to_returns => val,
+
+        (Type::Bytes(_) | Type::Int(_) | Type::Uint(_) | Type::Value, Type::Address(_)) => {
+            let llvm_ty = bin.llvm_type(from, ns);
+
+            let src = bin.build_alloca(function, llvm_ty, "dest");
+
+            bin.builder.build_store(src, val.into_int_value());
+
+            let dest = bin.build_alloca(function, bin.address_type(ns), "address");
+
+            let len = bin
+                .context
+                .i32_type()
+                .const_int(ns.address_length as u64, false);
 
-        bin.builder.build_call(
-            bin.module.get_function("__leNtobeN").unwrap(),
-            &[src.into(), dest.into(), len.into()],
-            "",
-        );
+            bin.builder.build_call(
+                bin.module.get_function("__leNtobeN").unwrap(),
+                &[src.into(), dest.into(), len.into()],
+                "",
+            );
 
-        bin.builder.build_load(bin.address_type(ns), dest, "val")
-    } else if let Type::Address(_) = from {
-        let llvm_ty = bin.llvm_type(to, ns);
+            bin.builder.build_load(bin.address_type(ns), dest, "val")
+        }
+        (Type::Address(_), Type::Bytes(_) | Type::Int(_) | Type::Uint(_) | Type::Value) => {
+            let llvm_ty = bin.llvm_type(to, ns);
 
-        let src = bin.build_alloca(function, bin.address_type(ns), "address");
+            let src = bin.build_alloca(function, bin.address_type(ns), "address");
 
-        bin.builder.build_store(src, val.into_array_value());
+            bin.builder.build_store(src, val.into_array_value());
 
-        let dest = bin.build_alloca(function, llvm_ty, "dest");
+            let dest = bin.build_alloca(function, llvm_ty, "dest");
 
-        let len = bin
-            .context
-            .i32_type()
-            .const_int(ns.address_length as u64, false);
+            let len = bin
+                .context
+                .i32_type()
+                .const_int(ns.address_length as u64, false);
 
-        bin.builder.build_call(
-            bin.module.get_function("__beNtoleN").unwrap(),
-            &[src.into(), dest.into(), len.into()],
-            "",
-        );
+            bin.builder.build_call(
+                bin.module.get_function("__beNtoleN").unwrap(),
+                &[src.into(), dest.into(), len.into()],
+                "",
+            );
 
-        bin.builder.build_load(llvm_ty, dest, "val")
-    } else if matches!(from, Type::Bool) && matches!(to, Type::Int(_) | Type::Uint(_)) {
-        bin.builder
+            bin.builder.build_load(llvm_ty, dest, "val")
+        }
+        (Type::Bool, Type::Int(_) | Type::Uint(_)) => bin
+            .builder
             .build_int_cast(
                 val.into_int_value(),
                 bin.llvm_type(to, ns).into_int_type(),
                 "bool_to_int_cast",
             )
-            .into()
-    } else if !from.is_contract_storage()
-        && from.is_reference_type(ns)
-        && matches!(to, Type::Uint(_))
-    {
-        bin.builder
+            .into(),
+        (_, Type::Uint(_)) if !from.is_contract_storage() && from.is_reference_type(ns) => bin
+            .builder
             .build_ptr_to_int(
                 val.into_pointer_value(),
                 bin.llvm_type(to, ns).into_int_type(),
                 "ptr_to_int",
             )
-            .into()
-    } else if to.is_reference_type(ns) && matches!(from, Type::Uint(_)) {
-        bin.builder
+            .into(),
+        (Type::Uint(_), _) if to.is_reference_type(ns) => bin
+            .builder
             .build_int_to_ptr(
                 val.into_int_value(),
                 bin.llvm_type(to, ns).ptr_type(AddressSpace::default()),
                 "int_to_ptr",
             )
-            .into()
-    } else if matches!((from, to), (Type::DynamicBytes, Type::Slice(_))) {
-        let slice_ty = bin.llvm_type(to, ns);
-        let slice = bin.build_alloca(function, slice_ty, "slice");
+            .into(),
+        (Type::DynamicBytes, Type::Slice(_)) => {
+            let slice_ty = bin.llvm_type(to, ns);
+            let slice = bin.build_alloca(function, slice_ty, "slice");
 
-        let data = bin.vector_bytes(val);
+            let data = bin.vector_bytes(val);
 
-        let data_ptr = bin
-            .builder
-            .build_struct_gep(slice_ty, slice, 0, "data")
-            .unwrap();
+            let data_ptr = bin
+                .builder
+                .build_struct_gep(slice_ty, slice, 0, "data")
+                .unwrap();
 
-        bin.builder.build_store(data_ptr, data);
+            bin.builder.build_store(data_ptr, data);
 
-        let len =
-            bin.builder
-                .build_int_z_extend(bin.vector_len(val), bin.context.i64_type(), "len");
+            let len =
+                bin.builder
+                    .build_int_z_extend(bin.vector_len(val), bin.context.i64_type(), "len");
 
-        let len_ptr = bin
-            .builder
-            .build_struct_gep(slice_ty, slice, 1, "len")
-            .unwrap();
+            let len_ptr = bin
+                .builder
+                .build_struct_gep(slice_ty, slice, 1, "len")
+                .unwrap();
 
-        bin.builder.build_store(len_ptr, len);
+            bin.builder.build_store(len_ptr, len);
 
-        bin.builder.build_load(slice_ty, slice, "slice")
-    } else {
-        val
+            bin.builder.build_load(slice_ty, slice, "slice")
+        }
+        (Type::Address(_), Type::Slice(_)) => {
+            let slice_ty = bin.llvm_type(to, ns);
+            let slice = bin.build_alloca(function, slice_ty, "slice");
+            let address = bin.build_alloca(function, bin.llvm_type(from, ns), "address");
+
+            bin.builder.build_store(address, val);
+
+            let data_ptr = bin
+                .builder
+                .build_struct_gep(slice_ty, slice, 0, "data")
+                .unwrap();
+
+            bin.builder.build_store(data_ptr, address);
+
+            let len = bin
+                .context
+                .i64_type()
+                .const_int(ns.address_length as u64, false);
+
+            let len_ptr = bin
+                .builder
+                .build_struct_gep(slice_ty, slice, 1, "len")
+                .unwrap();
+
+            bin.builder.build_store(len_ptr, len);
+
+            bin.builder.build_load(slice_ty, slice, "slice")
+        }
+        (Type::Bytes(bytes_length), Type::Slice(_)) => {
+            let llvm_ty = bin.llvm_type(from, ns);
+            let src = bin.build_alloca(function, llvm_ty, "src");
+
+            bin.builder.build_store(src, val.into_int_value());
+
+            let dest = bin.build_alloca(
+                function,
+                bin.context.i8_type().array_type((*bytes_length).into()),
+                "dest",
+            );
+
+            bin.builder.build_call(
+                bin.module.get_function("__leNtobeN").unwrap(),
+                &[
+                    src.into(),
+                    dest.into(),
+                    bin.context
+                        .i32_type()
+                        .const_int((*bytes_length).into(), false)
+                        .into(),
+                ],
+                "",
+            );
+
+            let slice_ty = bin.llvm_type(to, ns);
+            let slice = bin.build_alloca(function, slice_ty, "slice");
+
+            let data_ptr = bin
+                .builder
+                .build_struct_gep(slice_ty, slice, 0, "data")
+                .unwrap();
+
+            bin.builder.build_store(data_ptr, dest);
+
+            let len = bin
+                .context
+                .i64_type()
+                .const_int((*bytes_length).into(), false);
+
+            let len_ptr = bin
+                .builder
+                .build_struct_gep(slice_ty, slice, 1, "len")
+                .unwrap();
+
+            bin.builder.build_store(len_ptr, len);
+
+            bin.builder.build_load(slice_ty, slice, "slice")
+        }
+        _ => unreachable!(),
     }
 }

+ 3 - 1
src/sema/expression/mod.rs

@@ -1190,7 +1190,9 @@ impl Expression {
             {
                 Ok(self.clone())
             }
-            (Type::DynamicBytes, Type::Slice(ty)) if ty.as_ref() == &Type::Bytes(1) => {
+            (Type::DynamicBytes | Type::Address(_) | Type::Bytes(_), Type::Slice(ty))
+                if ty.as_ref() == &Type::Bytes(1) =>
+            {
                 Ok(Expression::Cast {
                     loc: *loc,
                     to: to.clone(),

+ 3 - 0
src/sema/types.rs

@@ -1620,6 +1620,7 @@ impl Type {
             Type::StorageRef(..) => ns.storage_type().bytes(ns),
             Type::Ref(ty) => ty.bytes(ns),
             Type::FunctionSelector => ns.target.selector_length(),
+            Type::UserType(ty) => ns.user_types[*ty].ty.bytes(ns),
             _ => panic!("type not allowed"),
         }
     }
@@ -1636,6 +1637,8 @@ impl Type {
             Type::Value => ns.value_length as u16 * 8,
             Type::StorageRef(..) => ns.storage_type().bits(ns),
             Type::Ref(ty) => ty.bits(ns),
+            Type::FunctionSelector => (ns.target.selector_length() * 8).into(),
+            Type::UserType(ty) => ns.user_types[*ty].ty.bits(ns),
             _ => panic!("type not allowed"),
         }
     }

+ 2 - 2
tests/codegen_testcases/solidity/scale.sol

@@ -91,12 +91,12 @@ contract CompactEncoding {
         // CHECK: branchcond (unsigned more (builtin ArrayLength ((arg #0))) > uint32 63), block3, block1
 
         // CHECK: block8: # small
-        // CHECK: writebuffer buffer:%abi_encoded.temp.26 offset:uint32 0 value:uint8((%temp.27 * uint32 4))
+        // CHECK: writebuffer buffer:%abi_encoded.temp.26 offset:uint32 0 value:(trunc uint8 (%temp.27 * uint32 4))
         // CHECK: ty:uint32 %temp.28 = uint32 1
         // CHECK: branch block12
 
         // CHECK: block9: # medium
-        // CHECK: writebuffer buffer:%abi_encoded.temp.26 offset:uint32 0 value:uint16(((%temp.27 * uint32 4) | uint32 1))
+        // CHECK: writebuffer buffer:%abi_encoded.temp.26 offset:uint32 0 value:(trunc uint16 ((%temp.27 * uint32 4) | uint32 1))
         // CHECK: ty:uint32 %temp.28 = uint32 2
         // CHECK: branch block12
 

+ 4 - 4
tests/solana.rs

@@ -1508,10 +1508,10 @@ impl VirtualMachine {
         self.stack = vec![cur];
     }
 
-    fn create_pda(&mut self, program_id: &Account) -> (Account, Vec<u8>) {
+    fn create_pda(&mut self, program_id: &Account, len: usize) -> (Account, Vec<u8>) {
         let mut rng = rand::thread_rng();
 
-        let mut seed = [0u8; 7];
+        let mut seed = vec![0u8; len];
 
         rng.fill(&mut seed[..]);
 
@@ -1522,12 +1522,12 @@ impl VirtualMachine {
         println!(
             "new empty account {} with seed {}",
             account.to_base58(),
-            hex::encode(seed)
+            hex::encode(&seed)
         );
 
         self.create_empty_account(&account, program_id);
 
-        (account, seed.to_vec())
+        (account, seed)
     }
 
     fn create_empty_account(&mut self, account: &Account, program_id: &Account) {

+ 15 - 1
tests/solana_tests/call.rs

@@ -702,7 +702,10 @@ fn pda() {
                     AccountMeta({pubkey: SYSVAR_RENT_PUBKEY, is_writable: false, is_signer: false})
                 ];
 
-                tokenProgramId.call{seeds: [ ["foo"], ["b", "a", "r"] ], accounts: metas}(instr);
+                bytes3 foo = "foo";
+                address addr = address"8dtukUTHTZoVQTA5i4UdC2z6A2b5yvnJhkzhYnwAk3Fm";
+
+                tokenProgramId.call{seeds: [ [ foo ] , ["b", "a", "r"], [addr], [foo, addr, "meh"] ], accounts: metas}(instr);
             }
         }"#,
     );
@@ -721,6 +724,17 @@ fn pda() {
             signers[1],
             create_program_address(&vm.stack[0].id, &[b"bar"])
         );
+        assert_eq!(
+            signers[2],
+            create_program_address(&vm.stack[0].id, &[b"quinquagintaquadringentilliardth"])
+        );
+        assert_eq!(
+            signers[3],
+            create_program_address(
+                &vm.stack[0].id,
+                &[b"fooquinquagintaquadringentilliardthmeh"]
+            )
+        );
     };
 
     let token = Pubkey(

+ 16 - 14
tests/solana_tests/create_contract.rs

@@ -149,7 +149,7 @@ fn simple_create_contract() {
         .try_into()
         .unwrap();
 
-    let seed = vm.create_pda(&program_id);
+    let seed = vm.create_pda(&program_id, 7);
     let payer = account_new();
 
     vm.account_data.insert(payer, AccountState::default());
@@ -404,8 +404,8 @@ fn two_contracts() {
         .try_into()
         .unwrap();
 
-    let seed1 = vm.create_pda(&program_id);
-    let seed2 = vm.create_pda(&program_id);
+    let seed1 = vm.create_pda(&program_id, 5);
+    let seed2 = vm.create_pda(&program_id, 5);
     let payer = account_new();
     vm.account_data.insert(seed1.0, AccountState::default());
     vm.account_data.insert(seed2.0, AccountState::default());
@@ -534,7 +534,7 @@ fn account_with_seed() {
     );
 
     let program_id = vm.stack[0].id;
-    let seed = vm.create_pda(&program_id);
+    let seed = vm.create_pda(&program_id, 7);
     let payer = account_new();
     vm.account_data.insert(payer, AccountState::default());
 
@@ -566,10 +566,9 @@ fn account_with_seed_bump() {
     let mut vm = build_solidity(
         r#"
         contract bar {
-
             @space(511 + 102)
             @payer(payer)
-            constructor(@seed bytes seed, @bump byte b) {}
+            constructor(@seed address seed, @seed bytes2 seed2, @bump byte b) {}
 
             function hello() public returns (bool) {
                 return true;
@@ -580,34 +579,37 @@ fn account_with_seed_bump() {
 
     let program_id = vm.stack[0].id;
 
-    let mut seed = vm.create_pda(&program_id);
-    let bump = seed.1.pop().unwrap();
+    let (address, full_seed) = vm.create_pda(&program_id, 35);
+    let bump = full_seed[34];
+    let seed_addr = &full_seed[0..32];
+    let seed2 = &full_seed[32..34];
     let payer = account_new();
     vm.account_data.insert(payer, AccountState::default());
 
     vm.function("new")
         .arguments(&[
-            BorshToken::Bytes(seed.1),
+            BorshToken::Address(seed_addr.try_into().unwrap()),
+            BorshToken::FixedBytes(seed2.to_vec()),
             BorshToken::Uint {
                 width: 8,
                 value: bump.into(),
             },
         ])
         .accounts(vec![
-            ("dataAccount", seed.0),
+            ("dataAccount", address),
             ("payer", payer),
             ("systemProgram", [0; 32]),
         ])
         .call();
 
     assert_eq!(
-        vm.account_data.get_mut(&seed.0).unwrap().data.len(),
+        vm.account_data.get_mut(&address).unwrap().data.len(),
         511 + 102
     );
 
     let ret = vm
         .function("hello")
-        .accounts(vec![("dataAccount", seed.0)])
+        .accounts(vec![("dataAccount", address)])
         .call()
         .unwrap();
 
@@ -700,7 +702,7 @@ fn create_child() {
     let payer = account_new();
     let program_id = vm.stack[0].id;
 
-    let seed = vm.create_pda(&program_id);
+    let seed = vm.create_pda(&program_id, 7);
     vm.account_data.insert(payer, AccountState::default());
     vm.account_data.insert(seed.0, AccountState::default());
 
@@ -768,7 +770,7 @@ contract Child {
 
     let payer = account_new();
     let program_id = vm.stack[0].id;
-    let seed = vm.create_pda(&program_id);
+    let seed = vm.create_pda(&program_id, 7);
     vm.account_data.insert(seed.0, AccountState::default());
     vm.account_data.insert(payer, AccountState::default());