Transferring assets between accounts and pallet - substrate

I'm trying to create a pallet that users can deposit assets into and withdraw from.
I've written the following code, but I'm not sure it's the best way to about things due to frame_system::RawOrigin::Root.into() being accessible by every runtime.
I'm still fairly new to Substrate and not sure this is exactly how it works, would love some guidance on the best design choice.
Making use of assets pallet to deposit:
<Assets::Module<T>>::transfer(origin, asset_id, RawOrigin::Root.into(), amount);
To Withdraw:
<Assets::Module<T>>::transfer(RawOrigin::Root.into(), asset_id, origin, amount);
Edit
A similar idea written in Solidity:
contract DepositWithdrawSend {
using SafeMath for uint256;
mapping (address => mapping (address => uint256)) public depositInfo;
address public sendPallet;
constructor(address _sendPallet) public {
sendPallet = _sendPallet;
}
function deposit(address _token, uint256 _amount) public {
IERC20(_token).transferFrom(msg.sender, address(this), amount);
depositInfo[_token][msg.sender] = depositInfo[_token][msg.sender].add(_amount);
}
function withdraw(address _token, uint256 _amount) public {
require(depositInfo[_token][msg.sender] >= _amount, "Over withdraw");
require(IERC20(_token).balanceOf(address(this)) >= _amount, "Not enough");
IERC20(_token).transfer(msg.sender, amount);
depositInfo[_token][msg.sender] = depositInfo[_token][msg.sender].sub(_amount);
}
function send(address _token, uint256 _amount) public {
require(IERC20(_token).balanceOf(address(this)) >= _amount, "Not enough");
IERC20(_token).transfer(sendPallet, amount);
}
}

We follow a pretty simple pattern to give pallets their own "account" for transferring balances to or anything else.
First you create a unique PalletId representing your pallet:
use frame_support::PalletId;
const MyPalletId: PalletId = PalletId(*b"replace_");
Then from here, you can generate an AccountId from this PalletId:
use sp_runtime::traits::AccountIdConversion;
/// These actually do computation. If you need to keep using them,
/// then make sure you cache the value and only call them once.
pub fn account_id() -> T::AccountId {
T::PalletId::get().into_account()
}
pub fn sub_account(seed: u16) -> T::AccountId {
// only use two byte prefix to support 16 byte account id (used by test)
// "modl" ++ "replace_" ++ "hi" is 14 bytes, and two bytes remaining for bounty index
T::PalletId::get().into_sub_account(("hi", id))
}
This pattern is used in the Treasury Pallet and others.

Related

Solidity and Etheruem: Handling events while using the factory pattern

I am in the beginning stages on learning Solidity and hardhat. I found a tutorial that seems pretty good and I was able to get the code to work from the example here: https://dev.to/dabit3/building-scalable-full-stack-apps-on-ethereum-with-polygon-2cfb. Let's say I wanted to expand this example and add factory pattern functionality to it. If a method is getting called through a factory method, how do I ensure events are propogated out past the factory method? In this case, how would I handle the event(s) in the sample.js file?
NFTMarketFactory.sol:
// contracts/Market.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.3;
import "./NFTMarket.sol";
import "#openzeppelin/contracts/security/ReentrancyGuard.sol";
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
contract NFTMarketFactory is ReentrancyGuard {
address address;
address owner;
constructor() {
owner = payable(msg.sender);
}
.
.
.
function createMarketItem(
address nftContract,
uint256 tokenId,
uint256 price
) public payable nonReentrant {
NFTMarket market = NFTMarket(address);
return market.createMarketItem{value: msg.value}(address, tokenId, price, msg.sender);
}
}
NFTMarket.sol
/* Places an item for sale on the marketplace */
function createMarketItem(
address nftContract,
uint256 tokenId,
uint256 price,
address sender
) public payable nonReentrant {
require(price > 0, "Price must be at least 1 wei");
require(msg.value == listingPrice, "Price must be equal to listing price");
_itemIds.increment();
uint256 itemId = _itemIds.current();
idToMarketItem[itemId] = MarketItem(
itemId,
nftContract,
tokenId,
payable(sender),
payable(address(0)),
price,
false
);
IERC721(nftContract).transferFrom(sender, address(this), tokenId);
emit MarketItemCreated(
itemId,
nftContract,
tokenId,
sender,
address(0),
price,
false
);
}
test/sample.js
.
.
.
/* create two tokens */
let token1 = await nft.createToken("https://www.mytokenlocation.com");
let token2 = await nft.createToken("https://www.mytokenlocation2.com");
/* put both tokens for sale */
await nftMarketFactory.createMerchandise(nftContractAddress, 1, auctionPrice, { value: listingPrice })
await nftMarketFactory.createMerchandise(nftContractAddress, 2, auctionPrice, { value: listingPrice })
const [_, buyerAddress] = await ethers.getSigners()
/* execute sale of token to another user */
await nftMarketFactory.connect(buyerAddress).createMarketSale(nftContractAddress, 1, { value: auctionPrice})
.
.
.

Smart Contracts - Chainlink VRF / #openzeppelin/truffle-upgrades compatibility

Right now i am transforming a basic smart contract into a upgradeable smart contract using #openzeppelin/truffle-upgrades
So i followed all required steps from the docs but one issue remains:
Truffle-Upgrades requires me to replace the constructor with an initializer which is fine for me, but not for the smart contracts imported into my own smart contract, sample:
pragma solidity 0.6.6;
import "#chainlink/contracts/src/v0.6/VRFConsumerBase.sol";
contract Sample is VRFConsumerBase {
address private owner;
bytes32 internal keyHash;
uint256 internal fee;
constructor(address _owner)
VRFConsumerBase(
0xa555fC018435bef5A13C6c6870a9d4C11DEC329C, // VRF Coordinator
0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06 // LINK Token
) public
{
keyHash = 0xcaf3c3727e033261d383b315559476f48034c13b18f8cafed4d871abe5049186;
fee = 0.1 * 10 ** 18; // 0.1 LINK (Varies by network)
owner = _owner;
}
...
And therefore truffle complains:
../#chainlink/contracts/src/v0.6/VRFConsumerBase.sol:182: Contract `VRFConsumerBase` has a constructor
Define an initializer instead
As it is a third party package i can not replace it :)
Are there any architectural tricks/configurations?
I went through pretty much all docs on chainlink/truffle but did not find a solution for this issue.
Thanks!
UPDATE 1:
First of all, i modified the VRFConsumerBase contract to: (i also removed the comments to keep it short..)
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "#chainlink/contracts/src/v0.6/vendor/SafeMathChainlink.sol";
import "#chainlink/contracts/src/v0.6/interfaces/LinkTokenInterface.sol";
import "#chainlink/contracts/src/v0.6/VRFRequestIDBase.sol";
abstract contract VRFConsumerBaseUpgradable is VRFRequestIDBase {
using SafeMathChainlink for uint256;
function fulfillRandomness(bytes32 requestId, uint256 randomness)
internal virtual;
function requestRandomness(bytes32 _keyHash, uint256 _fee, uint256 _seed)
internal returns (bytes32 requestId)
{
LINK.transferAndCall(vrfCoordinator, _fee, abi.encode(_keyHash, _seed));
uint256 vRFSeed = makeVRFInputSeed(_keyHash, _seed, address(this), nonces[_keyHash]);
nonces[_keyHash] = nonces[_keyHash].add(1);
return makeRequestId(_keyHash, vRFSeed);
}
// removed immutable keyword <--
LinkTokenInterface internal LINK;
// removed immutable keyword <--
address private vrfCoordinator;
mapping(bytes32 /* keyHash */ => uint256 /* nonce */) private nonces;
// replaced constructor with initializer <--
function initialize(address _vrfCoordinator, address _link) public {
vrfCoordinator = _vrfCoordinator;
LINK = LinkTokenInterface(_link);
}
function rawFulfillRandomness(bytes32 requestId, uint256 randomness) external {
require(msg.sender == vrfCoordinator, "Only VRFCoordinator can fulfill");
fulfillRandomness(requestId, randomness);
}
}
What did i do:
I replaced the constructor with an initializer
I removed the immutable keyword from the state variables
Next, i used the Initializable contract from #openzeppelin/contracts-upgradeable in my file system to prevent the smart contract executing the initializer more than once:
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
abstract contract Initializable {
bool private _initialized;
bool private _initializing;
modifier initializer() {
require(_initializing || !_initialized, "Initializable: contract is already initialized");
bool isTopLevelCall = !_initializing;
if (isTopLevelCall) {
_initializing = true;
_initialized = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
}
}
}
Important:
I did not import the Initializable contract via the import statement in solidity.
Instead i copied the source code manually and set the compiler to 0.6.12 because #openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol is running on 0.8.x
Finally, i updated my contract to implement the Initializable and the new VRFConsumerBaseUpgradable contract:
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "./Initializable.sol";
import "./VRFConsumerBaseUpgradable.sol";
contract Sample is Initializable, VRFConsumerBaseUpgradable {
bytes32 internal keyHash;
uint256 internal fee;
address private owner;
function initialize(address _owner)
public
initializer
{
VRFConsumerBaseUpgradable.initialize(
0xa555fC018435bef5A13C6c6870a9d4C11DEC329C, // VRF Coordinator
0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06 // LINK Token
);
keyHash = 0xcaf3c3727e033261d383b315559476f48034c13b18f8cafed4d871abe5049186;
fee = 0.1 * 10 ** 18;
owner = _owner;
}
function getRandomNumber(uint256 userProvidedSeed) public returns (bytes32 requestId) {
require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK - fill contract with faucet");
return requestRandomness(keyHash, fee, userProvidedSeed);
}
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
// logic
}
...
}
I tested an initial migration as well as an upgrade via truffle and it both worked so i think this is fine and i am leaving this for future researchers..
What do you think? Should i create a merge request for VRFConsumerBaseUpgradable?

How to write data back to storage?

I have a method called changePlaceName and i know it is working but after i call getPlaces to see the changes, i don't see the new place name instead i see the name when i created a new place.
this is changePlaceName
export function changePlaceName(placeId: u32, placeName: PlaceName): void {
assert(placeId >= 0, 'Place ID must be >= 0');
const place = Place.find(placeId);
logging.log(place.name); //gives "Galata Tower"
place.name = placeName;
logging.log(place.name); // gives "New Galata Tower"
}
I need to save it somehow but I don't know how to do it.
I also tried this way;
export function changePlaceName(placeId: u32, placeName: string): void {
assert(placeId >= 0, 'Place ID must be >= 0');
const place = Place.find(placeId);
logging.log(place.name);
place.name = placeName;
let newPlace = storage.get<string>(placeName, 'new galata tower');
storage.set<string>(placeName, newPlace);
logging.log('New place is now: ' + newPlace);
}
Now my visual code is complaining about the newPlace inside the storage.set
How do I fix it?
What is the code of Place.find? I assume you are using a persistent map under the hood.
Is there a Place.set? You need to store the Place back to the same key used to find it.
because you're using some kind of class to manage the concept of "Place", why not add an instance method to that class to save() the place once you've changed it's name?
would help if you also posted your code for Place here, by the way
my guess is that it looks something like this?
!note: this is untested code
#nearBindgen
class Place {
private id: number | null
private name: string
static find (placeId: number): Place {
// todo: add some validation for placeId here
const place = places[placeId]
place.id = placeId
return place
}
// here is the instance method that can save this class
save(): bool {
places[this.id] = this
}
}
// a collection of places where placeId is the index
const places = new PersistentVector<Place>("p")

rxjs BehaviorSubject that emits via distinctUntilChanged

I'm wanting to implement an Observable / Subject with 3 particular attributes
Remember last emitted value and be able to surface it via a getter (BehaviorSubject)
Only emit when value changes
It must have a strong type such that the getter is known to be available by a consumer (aka. BehaviorSubject.getValue())
I'm thinking of just extending BehaviorSubject but want to make sure I'm not introducing any potential gotchas based on my novice understanding.
export class DistinctUntilChangedBehaviorSubject<T, TValue> extends BehaviorSubject<T> {
constructor(
initialValue: T,
private _distinctKeySelector?: (value: T) => TValue,
private _comparer?: _Comparer<TValue, boolean>
) {
super(initialValue);
}
public subscribe() {
// I'm particularly interested in knowing if this has any gotchas.
// Mostly things like creating subscriptions that don't get disposed as expected.
return super.distinctUntilChanged(
this._distinctKeySelector,
this._comparer
).subscribe.apply(this, arguments);
}
}
So 2 questions:
Does this seem like a reasonable approach / are there any gotchas here?
Is there another preferred way of doing this?
I do not know really why, but I tend to prefer composition over extension.
So I would do something along these lines
import {BehaviorSubject} from 'rxjs';
export class BehaviourSubjectAugmented<T> {
bs: BehaviorSubject<T>;
constructor(initialValue: T, private comparer: (p: T, q: T) => boolean) {
this.bs = new BehaviorSubject(initialValue);
}
getValue() {
return this.bs.getValue();
}
asObservable() {
return this.bs.asObservable()
.distinctUntilChanged(this.comparer);
}
complete() {
return this.bs.complete();
}
next(value: T) {
return this.bs.next(value);
}
}
Turns out my original idea causes a call stack exceeded issue. I'm assuming that distinctUntilChanged must call subscribe internally thus causing infinite recursion.
I ended up finding a simpler way to get what I needed by simply adding a method to an ISubject instance.
function distinctUntilChangedBehaviorSubject(
initialValue: number
): ISubject<number> & { getValue(): number } {
const observer = new BehaviorSubject<number>(initialValue);
const observable = observer.distinctUntilChanged();
const subject: ISubject<number> = Subject.create(
observer,
observable
);
return Object.assign(
subject,
{
getValue: () => observer.getValue()
}
);
}

How can I return an array of struct in solidity?

I am designing a solution for an ethereum smart contract that does bidding. The use-case includes reserving a name eg. "myName" and assigning to an address. And then, people can bid for that name (in this case myName). There can be multiple such biddings happening for multiple names.
struct Bid {
address bidOwner;
uint bidAmount;
bytes32 nameEntity;
}
mapping(bytes32 => Bid[]) highestBidder;
So, as you can see above, Bid struct holds data for one bidder, similarly, the key (eg. myName) in the mapping highestBidder points to an array of such bidders.
Now, I am facing a problem when I try to return something like highestBidder[myName].
Apparently, solidity does not support returning an array of structs (dynamic data). I either need to rearchitect my solution or find some workaround to make it work.
If you guys have any concerns regarding the question, please let me know, I will try to make it clear.
I am stuck here any help would be appreciated.
As you mentioned, this is not yet supported in Solidity. The powers that be are planning on changing it so you can, but for now, you have to retrieve the number of elements and then retrieve the decomposed struct as a tuple.
function getBidCount(bytes32 name) public constant returns (uint) {
return highestBidder[name].length;
}
function getBid(bytes32 name, uint index) public constant returns (address, uint, bytes32) {
Bid storage bid = highestBidder[name][index];
return (bid.bidOwner, bid.bidAmount, bid.nameEntity);
}
Edit to address question in comment regarding storage vs memory in this case
Local storage variables are pointers to state variables (which are always in storage). From the Solidity docs:
The type of the local variable x is uint[] storage, but since storage is not dynamically allocated, it has to be assigned from a state variable before it can be used. So no space in storage will be allocated for x, but instead it functions only as an alias for a pre-existing variable in storage.
This is referring to an example where the varable used is uint[] x. Same applies to my code with Bid bid. In other words, no new storage is being created.
In terms of cost:
getBid("foo", 0) using Bid memory bid:
getBid("foo", 0) using Bid storage bid:
In this case, storage is cheaper.
Return an array of struct in solidity?
In below function getBid returns array of bid structure.
contract BidHistory {
struct Bid {
address bidOwner;
uint bidAmount;
bytes32 nameEntity;
}
mapping (uint => Bid) public bids;
uint public bidCount;
constructor() public {
bidCount = 0;
storeBid("address0",0,0);
storeBid("address1",1,1);
}
function storeBid(address memory _bidOwner, uint memory _bidAmount, bytes32 memory _nameEntity) public {
bids[tripcount] = Bid(_bidOwner, _bidAmount,_nameEntity);
bidCount++;
}
//return Array of structure
function getBid() public view returns (Bid[] memory){
Bid[] memory lBids = new Bid[](tripcount);
for (uint i = 0; i < bidCount; i++) {
Bid storage lBid = bids[i];
lBids[i] = lBid;
}
return lBids;
}
}
About "returning an array of structs"... just a small workaround in order to return an array of structs extracted from medium
pragma solidity ^0.4.13;
contract Project
{
struct Person {
address addr;
uint funds;
}
Person[] people;
function getPeople(uint[] indexes)
public
returns (address[], uint[]) {
address[] memory addrs = new address[](indexes.length);
uint[] memory funds = new uint[](indexes.length);
for (uint i = 0; i < indexes.length; i++) {
Person storage person = people[indexes[i]];
addrs[i] = person.addr;
funds[i] = person.funds;
}
return (addrs, funds);
}
}
The uint[] index parameters should contain the indexes that you want to access.
Best

Resources