Cannot use u128 to represent money/price in smart contract model - assemblyscript

import { context, u128, storage, logging, PersistentUnorderedMap } from "near-sdk-as";
#nearBindgen
export class MyItem{
id: u64;
type: u32;
price: u128; // This is causing the deserialization error
}
When including this fragment of code in my smart contract, compiling, deploying to testnet and calling any method on it, I receive: "Error happened while deserializing the module"
When I change price:u128 to price:u64, it all starts to work, no more error.
What is the best way to represent monetary value of something (for example NFT) in a smart contract model? Is u128 the wrong type? If I should use some other type - how do I convert it to u128 before transferring it?

Related

Using "ERC20" and interfaces names as variable (in functions)

I'm having problems wrapping my head around ERC20 and interfaces passed as variables.
Examples:
function foo(ERC20 _project) external { //some code }
function fuu(IERC4626 _project) external { ERC20(_project.asset()) }
What does this exactly do and how do we use it?
Haven't been able to find any explanation next to: Using interface name as Variable type. A link to the explanation is also fine.
My guesses:
Example 1: we input an address type which simply refers to an ERC20 contract, which we can use then as:
Project project = _project;
project.withdraw();
If above is true, why don't we simply take in an address type in the function instead of "ERC20" type?
Example 2: Calls an ERC20 contract which implements the IERC4626 interface? If so, how is that checked? tbh I have no idea.

Should an Error with a source include that source in the Display output?

I have an error type that impls the Error trait, and it wraps an underlying error cause, so the source method returns Some(source). I want to know whether the Display impl on my error type should include a description of that source error, or not.
I can see two options:
Yes, include source in Display output, e.g. "Error opening database: No such file"
This makes it easy to print a whole error chain just by formatting with "{}" but impossible to only display the error itself without the underlying chain of source errors. Also it makes the source method a bit pointless and gives client code no choice on how to format separation between each error in the chain. Nevertheless this choice seems common enough in example code I have found.
No, just print the error itself e.g. "Error opening database" and leave it to client code to traverse and display source if it wants to include that in the output.
This gives client code the choice of whether to display just the surface error or the whole chain, and in the latter case how to format separation between each error in the chain. It leaves client code with the burden of iterating through the chain, and I haven't yet fallen upon a canonical utility for conveniently formatting an error chain from errors that each only Display themselves excluding source. (So of course I have my own.)
The snafu crate (which I really like) seems to hint at favoring option 2, in that an error variant with a source field but no display attribute defaults to formatting Display output that does not include source.
Maybe my real question here is: What is the purpose of the source method? Is it to make formatting error chains more flexible? Or should Display really output everything that should be user-visible about an error, and source is just there for developer-visible purposes?
I would love to see some definitive guidance on this, ideally in the documentation of the Error trait.
#[derive(Debug)]
enum DatabaseError {
Opening { source: io::Error },
}
impl Error for DatabaseError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
DataBaseError::Opening { source } => Some(source),
}
}
}
impl fmt::Display for DatabaseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
DatabaseError::Opening { source } => {
// ??? Should we include the source?
write!(f, "Error opening database: {}", source)
// ??? Or should we leave it to the caller to call .source()
// if they want to include that in the error description?
write!(f, "Error opening database")
}
}
}
}
The two options of whether to print the source error on a Display implementation creates two schools of design. This answer will explain the two while objectively stating their key differences and clarifying a few possible misconceptions in the way.
Design 1: Yes, include source on your Display impl
Example with SNAFU:
#[derive(Debug, Snafu)]
enum Error {
#[snafu(display("Could not read data set token: {}", source))]
ReadToken {
#[snafu(backtrace)]
source: ReadDataSetError,
},
}
The key advantage, as already mentioned in the question, is that providing the full amount of information is as simple as just printing the error value.
eprintln!("[ERROR] {}", err);
It is simple and easy, requiring no helper functions for reporting the error, albeit with the lack of presentation flexibility. Without string manipulation, a chain of colon-separated errors is what you will always get.
[ERROR] Could not read data set token: Could not read item value: Undefined value length of element tagged (5533,5533) at position 3548
Design 2: No, leave out source on your Display impl
#[derive(Debug, Snafu)]
enum Error {
#[snafu(display("Could not read data set token"))]
ReadToken {
#[snafu(backtrace)]
source: ReadDataSetError,
},
}
While this will not give you the full information with a single line print like before, you can leave that task to a project-wide error reporter. This also grants the consumer of the API greater flexibility on error presentation.
A simple example follows. Additional logic would be required for presenting the error's backtrace.
fn report<E: 'static>(err: E)
where
E: std::error::Error,
E: Send + Sync,
{
eprintln!("[ERROR] {}", err);
if let Some(cause) = err.source() {
eprintln!();
eprintln!("Caused by:");
for (i, e) in std::iter::successors(Some(cause), |e| e.source()).enumerate() {
eprintln!(" {}: {}", i, e);
}
}
}
Playground
It's also worth considering the interest of integrating with opinionated libraries. That is, certain crates in the ecosystem may have already made an assumption about which option to choose. In anyhow, error reports will already traverse the error's source chain by default. When using anyhow for error reporting, you should not be adding source, otherwise you may come up with an irritating list of repeated messages:
[ERROR] Could not read data set token: Could not read item value: Undefined value length of element tagged (5533,5533) at position 3548
Caused by:
0: Could not read item value: Undefined value length of element tagged (5533,5533) at position 3548
1: Undefined value length of element tagged (5533,5533) at position 3548
Likewise, the eyre library provides a customizable error reporting abstraction, but existing error reporters in the eyre crate ecosystem also assume that the source is not printed by the error's Display implementation.
So, which one?
Thanks to the efforts of the Error Handling project group, a key guideline regarding the implementation of Display was proposed in early 2021:
An error type with a source error should either return that error via source or include that source's error message in its own Display output, but never both.
That would be the second design: avoid appending the source's error message in your Display implementation. For SNAFU users, this currently means that applications will need to bring in an error reporter until one is made available directly in the snafu crate. As the ecosystem is yet to mature around this guideline, one may still find error utility crates lacking support for error reporting in this manner.
In either case...
This decision only plays a role in error reporting, not in error matching or error handling in some other manner. The existence of the source method establishes a chain-like structure on all error types, which can be exploited in pattern matching and subsequent flow control of the program.
The Error::source method has a purpose in the ecosystem, regardless of how errors are reported.
In addition, it's ultimately up to the developers to choose how to design their errors and respective Display implementations, although once it starts integrating with other components, following the guideline will be the right way towards consistent error reporting.
What about Rust API guidelines?
The Rust API guidelines do not present an opinion about Display in errors, other than C-GOOD-ERR, which only states that the error type's Display message should be "lowercase without trailing punctuation, and typically concise". There is a pending proposal to update this guideline, instructing developers to exclude source in their Display impl. However, the pull request was created before the guideline was proposed, and has not been updated since then (at the time of writing).
See also:
The error handling protect group (GitHub)
Inside Rust Blog post 2021-07-01: What the error handling project group is working towards

Reuse the description of an existing Error when creating a new Error

I have the following code in Rust, which does not compile, but shows the intent of what I'd like to do.
pub fn parse(cursor: &mut io::Cursor<&[u8]>) -> io::Result<Ack> {
use self::byteorder::{BigEndian, ReadBytesExt};
use self::core::error::Error;
match cursor.read_u16::<BigEndian>() {
Err(byteorder::Error::Io(error)) => Err(error),
Err(error) =>
Err(io::Error::new(io::ErrorKind::Other, error.description(),
None)),
Ok(value) => Ok(Ack { block_number: value })
}
}
Essentially, I want to take the error description of an error returned by the byteorder library and use it to create the description of an error I'll pass back to the user of my library. This fails with packets.rs:166:58: 166:63 error:errordoes not live long enough, and I understand why.
The byteorder library solves this issue by wrapping an std::io::Result in the byteorder::Error::Io constructor. However, I don't want to take this route because I'd have to define my own error type that wraps either an std::io::Error or a byteorder::Error. It seems to me that my users shouldn't know or care that I use the byteorder library, and it shouldn't be part of my interface.
I'm a Rust newbie and don't yet know the idioms and best practices of the language and design. What are my options for dealing with this?
Your problem is in fact in that io::Error::new()'s second parameter is &'static str, while byteorder::Error::description() returns a &'a str where 'a is lifetime of the error object itself which is less than 'static. Hence you can't use it for io::Error's description.
The simplest fix would be moving byteorder::Error description to detail field of io::Error:
Err(error) =>
Err(io::Error::new(
io::ErrorKind::Other,
"byteorder error",
Some(error.description().to_string())
)),
However, you should seriously consider making a custom wrapper error type which encapsulates all "downstream" errors. With properly written FromError instances you should be able to write something like
try!(cursor.read_u16::<BigEndian>()
.map(|value| Ack { block_number: value }))
instead of your whole match. Custom error wrappers will also help you when your program grows and more "downstream" error sources appear - you could just add new enum variants and/or FromError implementations to support these new errors.
I cannot test your code so I can't be sure. Isn't the ref keyword enough?
Err(byteorder::Error::Io(ref error)) => Err(error),

Dart serialization error: Invalid reference

I have a wrapped Serialization class from the serialization package in my class MySerialization. In the constroctor of MySerialization, I add a bunch of rules. Consumer classes have seperate instances of the wrapper MySerialization class to (de)serialize objects.
This setup, with a seperate instance of MySerialization in consumer classes throws an error in the Reference class constructor:
Reference(this.parent, this.ruleNumber, this.objectNumber) {
if (ruleNumber == null || objectNumber == null) {
throw new SerializationException("Invalid Reference");
}
if (parent.rules.length < ruleNumber) {
throw new SerializationException("Invalid Reference"); // <---- here
}
}
thus spawnes error in the console
Breaking on exception: SerializationException(Invalid Reference)
This means a rule cannot be found which is referenced. The starnge thing howver is, that I have the same rules applied in all Serialization instances through the MySerialization wrapper.
I tried serializing with only one instance of MySerialization. This does not spawn the error. When I debug in DartEditor, I get the <optimized out> message in the debugger window.
I have CustomRule subclasses rules defined. The behavior does not change when I enable/disabled these CustomRules
What cuases the invalid reference, and how to solve & workaround this error?
Dart Editor version 1.5.3.release (STABLE)
Dart SDK version 1.5.3
It's difficult to answer without a little more detail on your setup. However, I'm going to guess that you're using the default setup in which it will automatically generate instances of BasicRule when it encounters a class that it doesn't know about, and those are added to the list of rules. Your other instance doesn't know about those, so it fails.
You can try examining (or just printing) the list of rules in your original serialization after it has written out the objects and see if this is the case.
To fix this, you would need to write rules for the other objects that are being serialized and weren't in your original list. Or you could use the "selfDescribing" option, in which case it will send the rules that were used along with the original. But that won't work if you have hard-coded custom rules which it can't serialize.

How to pass a struct parameter using TCOM in Tcl

I've inherited a piece of custom test equipment with a control library built in a COM object, and I'm trying to connect it to our Tcl test script library. I can connect to the DLL using TCOM, and do some simple control operations with single int parameters. However, certain features are controlled by passing in a C/C++ struct that contains the control blocks, and attempting to use them in TCOM is giving me an error 0x80020005 {Type mismatch.}. The struct is defined in the .idl file, so it's available to TCOM to use.
The simplest example is a particular call as follows:
C++ .idl file:
struct SourceScaleRange
{
float MinVoltage;
float MaxVoltage;
};
interface IAnalogIn : IDispatch{
...
[id(4), helpstring("method GetAdcScaleRange")] HRESULT GetAdcScaleRange(
[out] struct SourceScaleRange *scaleRange);
...
}
Tcl wrapper:
::tcom::import [file join $::libDir "PulseMeas.tlb"] ::char
set ::characterizer(AnalogIn) [::char::AnalogIn]
set scaleRange ""
set response [$::characterizer(AnalogIn) GetAdcScaleRange scaleRange]
Resulting error:
0x80020005 {Type mismatch.}
while executing
"$::characterizer(AnalogIn) GetAdcScaleRange scaleRange"
(procedure "charGetAdcScaleRange" line 4)
When I dump TCOM's methods, it knows of the name of the struct, at least, but it seems to have dropped the struct keyword. Some introspection code
set ifhandle [::tcom::info interface $::characterizer(AnalogIn)]
puts "methods: [$ifhandle methods]"
returns
methods: ... {4 VOID GetAdcScaleRange {{out {SourceScaleRange *} scaleRange}}} ...
I don't know if this is meaningful or not.
At this point, I'd be happy to get any ideas on where to look next. Is this a known TCOM limitation (undocumented, but known)? Is there a way to pre-process the parameter into an appropriate format using tcom? Do I need to force it into a correctly sized block of memory via binary format by manual construction? Do I need to take the DLL back to the original developer and have him pull out all the struct parameters? (Not likely to happen, in this reality.) Any input is good input.