Browse Source

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 years ago
parent
commit
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
 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,
 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
 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:
 .. _value_transfer:
 
 

+ 1 - 1
integration/solana/UserStats.sol

@@ -10,7 +10,7 @@ contract UserStats {
     @payer(wallet)
     @payer(wallet)
     @seed("user-stats")
     @seed("user-stats")
     @space(250)
     @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;
         name = _name;
         level = _level;
         level = _level;
         bump = _bump;
         bump = _bump;

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

@@ -1,17 +1,17 @@
 // SPDX-License-Identifier: Apache-2.0
 // 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 { utils } from '@coral-xyz/anchor';
 import expect from "expect";
 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
     // 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
     // on a unique account on chain, resembling a hash table. This is an example for achieving
     // so with Solidity.
     // so with Solidity.
 
 
     it('Table functions', async function test_table() {
     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.
         // A user's public key will be the key for the hash table in this example.
         const myUser = Keypair.generate();
         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
         // We create the account to hold the user's related information. The generated PDA becomes the
         // data account for our contract.
         // data account for our contract.
         // If a contract for `userStatsPDA` already exists, this function will fail.
         // 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({
             .accounts({
-                    dataAccount: userStatsPDA,
-                    wallet: payer.publicKey,
+                dataAccount: userStatsPDA,
+                wallet: payer.publicKey,
             })
             })
             .signers([payer])
             .signers([payer])
             .rpc();
             .rpc();

+ 1 - 1
src/codegen/cfg.rs

@@ -933,7 +933,7 @@ impl ControlFlowGraph {
                     .collect::<Vec<String>>()
                     .collect::<Vec<String>>()
                     .join(", ")
                     .join(", ")
             ),
             ),
-            Expression::InternalFunctionCfg { cfg_no } => {
+            Expression::InternalFunctionCfg { cfg_no, .. } => {
                 format!("function {}", contract.cfg[*cfg_no].name)
                 format!("function {}", contract.cfg[*cfg_no].name)
             }
             }
             Expression::ReturnData { .. } => "(external call return data)".to_string(),
             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 {
             Instr::WriteBuffer {
                 buf: buffer.clone(),
                 buf: buffer.clone(),
                 offset: offset.clone(),
                 offset: offset.clone(),
-                value: Expression::Cast {
+                value: Expression::Trunc {
                     loc: Codegen,
                     loc: Codegen,
                     ty: Uint(8),
                     ty: Uint(8),
                     expr: mul.clone().into(),
                     expr: mul.clone().into(),
@@ -371,7 +371,7 @@ fn encode_compact(
             Instr::WriteBuffer {
             Instr::WriteBuffer {
                 buf: buffer.clone(),
                 buf: buffer.clone(),
                 offset: offset.clone(),
                 offset: offset.clone(),
-                value: Expression::Cast {
+                value: Expression::Trunc {
                     loc: Codegen,
                     loc: Codegen,
                     ty: Uint(16),
                     ty: Uint(16),
                     expr: mul.into(),
                     expr: mul.into(),

+ 2 - 0
src/codegen/expression.rs

@@ -486,6 +486,7 @@ pub fn expression(
         ast::Expression::InternalFunction {
         ast::Expression::InternalFunction {
             function_no,
             function_no,
             signature,
             signature,
+            ty,
             ..
             ..
         } => {
         } => {
             let function_no = if let Some(signature) = signature {
             let function_no = if let Some(signature) = signature {
@@ -495,6 +496,7 @@ pub fn expression(
             };
             };
 
 
             Expression::InternalFunctionCfg {
             Expression::InternalFunctionCfg {
+                ty: ty.clone(),
                 cfg_no: ns.contracts[contract_no].all_functions[function_no],
                 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>,
         expr: Box<Expression>,
     },
     },
     InternalFunctionCfg {
     InternalFunctionCfg {
+        ty: Type,
         cfg_no: usize,
         cfg_no: usize,
     },
     },
     Keccak256 {
     Keccak256 {
@@ -839,7 +840,8 @@ impl RetrieveType for Expression {
             | Expression::AllocDynamicBytes { ty, .. }
             | Expression::AllocDynamicBytes { ty, .. }
             | Expression::BytesCast { ty, .. }
             | Expression::BytesCast { ty, .. }
             | Expression::RationalNumberLiteral { ty, .. }
             | Expression::RationalNumberLiteral { ty, .. }
-            | Expression::Subscript { ty, .. } => ty.clone(),
+            | Expression::Subscript { ty, .. }
+            | Expression::InternalFunctionCfg { ty, .. } => ty.clone(),
 
 
             Expression::BoolLiteral { .. }
             Expression::BoolLiteral { .. }
             | Expression::MoreEqual { .. }
             | Expression::MoreEqual { .. }
@@ -859,7 +861,6 @@ impl RetrieveType for Expression {
 
 
             Expression::AdvancePointer { .. } => Type::BufferPointer,
             Expression::AdvancePointer { .. } => Type::BufferPointer,
             Expression::FormatString { .. } => Type::String,
             Expression::FormatString { .. } => Type::String,
-            Expression::InternalFunctionCfg { .. } => Type::Unreachable,
             Expression::Poison => unreachable!("Expression does not have a type"),
             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
 /// Other types (e.g. bytes) is not relevant for strength reduce. Bools are only
 /// tracked so we can following branching after integer compare.
 /// tracked so we can following branching after integer compare.
 fn track(ty: &Type) -> bool {
 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
 // A variable can

+ 185 - 71
src/emit/expression.rs

@@ -1815,7 +1815,7 @@ pub(super) fn expression<'a, T: TargetRuntime<'a> + ?Sized>(
                 .into()
                 .into()
         }
         }
         Expression::Builtin { .. } => target.builtin(bin, e, vartab, function, ns),
         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_global_value()
             .as_pointer_value()
             .as_pointer_value()
             .into(),
             .into(),
@@ -1904,106 +1904,220 @@ fn runtime_cast<'a>(
     val: BasicValueEnum<'a>,
     val: BasicValueEnum<'a>,
     ns: &Namespace,
     ns: &Namespace,
 ) -> BasicValueEnum<'a> {
 ) -> BasicValueEnum<'a> {
-    if matches!(from, Type::Address(_) | Type::Contract(_))
-        && matches!(to, Type::Address(_) | Type::Contract(_))
-    {
+    match (from, to) {
         // no conversion needed
         // 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(
             .build_int_cast(
                 val.into_int_value(),
                 val.into_int_value(),
                 bin.llvm_type(to, ns).into_int_type(),
                 bin.llvm_type(to, ns).into_int_type(),
                 "bool_to_int_cast",
                 "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(
             .build_ptr_to_int(
                 val.into_pointer_value(),
                 val.into_pointer_value(),
                 bin.llvm_type(to, ns).into_int_type(),
                 bin.llvm_type(to, ns).into_int_type(),
                 "ptr_to_int",
                 "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(
             .build_int_to_ptr(
                 val.into_int_value(),
                 val.into_int_value(),
                 bin.llvm_type(to, ns).ptr_type(AddressSpace::default()),
                 bin.llvm_type(to, ns).ptr_type(AddressSpace::default()),
                 "int_to_ptr",
                 "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())
                 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 {
                 Ok(Expression::Cast {
                     loc: *loc,
                     loc: *loc,
                     to: to.clone(),
                     to: to.clone(),

+ 3 - 0
src/sema/types.rs

@@ -1620,6 +1620,7 @@ impl Type {
             Type::StorageRef(..) => ns.storage_type().bytes(ns),
             Type::StorageRef(..) => ns.storage_type().bytes(ns),
             Type::Ref(ty) => ty.bytes(ns),
             Type::Ref(ty) => ty.bytes(ns),
             Type::FunctionSelector => ns.target.selector_length(),
             Type::FunctionSelector => ns.target.selector_length(),
+            Type::UserType(ty) => ns.user_types[*ty].ty.bytes(ns),
             _ => panic!("type not allowed"),
             _ => panic!("type not allowed"),
         }
         }
     }
     }
@@ -1636,6 +1637,8 @@ impl Type {
             Type::Value => ns.value_length as u16 * 8,
             Type::Value => ns.value_length as u16 * 8,
             Type::StorageRef(..) => ns.storage_type().bits(ns),
             Type::StorageRef(..) => ns.storage_type().bits(ns),
             Type::Ref(ty) => ty.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"),
             _ => 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: branchcond (unsigned more (builtin ArrayLength ((arg #0))) > uint32 63), block3, block1
 
 
         // CHECK: block8: # small
         // 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: ty:uint32 %temp.28 = uint32 1
         // CHECK: branch block12
         // CHECK: branch block12
 
 
         // CHECK: block9: # medium
         // 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: ty:uint32 %temp.28 = uint32 2
         // CHECK: branch block12
         // CHECK: branch block12
 
 

+ 4 - 4
tests/solana.rs

@@ -1508,10 +1508,10 @@ impl VirtualMachine {
         self.stack = vec![cur];
         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 rng = rand::thread_rng();
 
 
-        let mut seed = [0u8; 7];
+        let mut seed = vec![0u8; len];
 
 
         rng.fill(&mut seed[..]);
         rng.fill(&mut seed[..]);
 
 
@@ -1522,12 +1522,12 @@ impl VirtualMachine {
         println!(
         println!(
             "new empty account {} with seed {}",
             "new empty account {} with seed {}",
             account.to_base58(),
             account.to_base58(),
-            hex::encode(seed)
+            hex::encode(&seed)
         );
         );
 
 
         self.create_empty_account(&account, program_id);
         self.create_empty_account(&account, program_id);
 
 
-        (account, seed.to_vec())
+        (account, seed)
     }
     }
 
 
     fn create_empty_account(&mut self, account: &Account, program_id: &Account) {
     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})
                     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],
             signers[1],
             create_program_address(&vm.stack[0].id, &[b"bar"])
             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(
     let token = Pubkey(

+ 16 - 14
tests/solana_tests/create_contract.rs

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