EIP-712 typed data signing and EIP-191 personal signing.
Build a typed data request, encode it for the JS port, decode the signature response.
Use the SignState machine to track the lifecycle of a single sign request.
import Dict
import Json.Encode as E
import Web3.Sign as Sign
permitRequest : T.Address -> T.Address -> BigInt -> Int -> Sign.TypedData
permitRequest owner spender value nonce =
Sign.typedData
{ domain =
{ name = Just "MyToken", version = Just "1"
, chainId = Just 369, verifyingContract = Just tokenAddress
, salt = Nothing
}
, types =
Dict.fromList
[ ( "Permit"
, [ { name = "owner", typeName = "address" }
, { name = "spender", typeName = "address" }
, { name = "value", typeName = "uint256" }
, { name = "nonce", typeName = "uint256" }
, { name = "deadline", typeName = "uint256" }
]
)
]
, primaryType = "Permit"
, message = E.object [ ... ]
}
-- Send via port:
web3Cmd (Sign.encode "permit-1" signerAddress (permitRequest owner spender value nonce))
-- Receive via port (use signatureDecoder for the raw sig string):
case D.decodeValue Sign.signatureDecoder incoming of
Ok sig -> -- sig is the 0x-prefixed signature
Err _ -> -- not a sign responseSign state machine -- tracks one in-flight request:
type SignState
= SignIdle
| SignPending String -- id
| Signed String String -- id, signature
| SignFailed String String -- id, error
| SignRejected String -- id
-- Usage:
( { model | signState = Sign.startSign "permit-1" model.signState }
, web3Cmd (Sign.encode "permit-1" addr request)
)
-- On port response:
newSignState = Sign.signUpdate signMsg model.signStateEIP-712 domain separator parameters. All fields are optional; include only those your contract's domain hash covers.
A single field in a struct type definition.
Construct a TypedData value.
Encode a TypedData request as a port command.
web3Cmd (Sign.encode "my-id" signerAddress request)The id is echoed back in the signed response so you can match
responses to requests when multiple signing requests are in flight.
Sign an arbitrary message with personal_sign (EIP-191).
Used for login flows and simple off-chain authentication. The wallet will display the raw message to the user before signing.
web3Cmd (Sign.personalSign "login-1" signerAddress "Sign in to MyDapp")Responses arrive on the same signed tag as EIP-712 -- use signatureDecoder.
Decode the signed response from the JS port.
case D.decodeValue Sign.signatureDecoder incoming of
Ok sig ->
-- sig : String -- the 0x-prefixed signature
Err _ ->
-- handle decode errorSigning state machine. Tracks the lifecycle of a single sign request.
SignIdle -- no sign request in progressSignPending id -- awaiting user approval for request idSigned id sig -- user approved; sig is the 0x-prefixed signatureSignFailed id err -- signing failed (wallet error, network error)SignRejected id -- user explicitly cancelled in wallet UIMessages from the JS sign port.
Transition from SignIdle to SignPending for the given correlation id.
All other states are unchanged (a sign already in flight is not replaced).
Update sign state from a port message. Terminal states never transition out.
True if the sign state is terminal (no more updates expected).
Verify an EIP-191 personal_sign signature by recovering the signer
address (personal_ecRecover).
web3Cmd
(Sign.verify
{ id = "login-verify-1"
, message = "Sign in to MyDapp"
, signature = sig
}
)The recovered address arrives on the recovered tag -- decode it with
recoveredDecoder and compare it against the address
that claims to have signed. Failures arrive on the standard failed tag.
This is a standalone encoder returning the port value directly (like
personalSign) rather than a new Cmd/Msg variant --
extending an exposed custom type is a MAJOR change under Elm's enforced
semver, so additive functions keep this MINOR-safe.
Decode the recovered response from a verify request.
case D.decodeValue Sign.recoveredDecoder incoming of
Ok { id, address } ->
-- address : String -- the 0x-prefixed recovered signer
Err _ ->
-- not a recovered responseStandalone (not a SignMsg variant) for the same additive-safety reason
as verify.
An EIP-712 typed data signing request.