diff --git a/common/json.c b/common/json.c
index c89d8af00..072d397b1 100644
--- a/common/json.c
+++ b/common/json.c
@@ -76,6 +76,21 @@ bool json_tok_double(const char *buffer, const jsmntok_t *tok, double *num)
 	return true;
 }
 
+bool json_tok_percent(const char *buffer, const jsmntok_t *tok, double *num)
+{
+	if (!json_tok_double(buffer, tok, num))
+		return false;
+
+	/* Ensure it is in the range [0.0, 100.0] */
+	if (!(0.0 <= *num))
+		return false;
+
+	if (!(*num <= 100.0))
+		return false;
+
+	return true;
+}
+
 bool json_tok_number(const char *buffer, const jsmntok_t *tok,
 		     unsigned int *num)
 {
diff --git a/common/json.h b/common/json.h
index 0c1cb12f3..2c4fa4f3c 100644
--- a/common/json.h
+++ b/common/json.h
@@ -39,6 +39,9 @@ bool json_tok_double(const char *buffer, const jsmntok_t *tok, double *num);
 bool json_tok_bitcoin_amount(const char *buffer, const jsmntok_t *tok,
 			     uint64_t *satoshi);
 
+/* Extract double in range [0.0, 100.0] */
+bool json_tok_percent(const char *buffer, const jsmntok_t *tok, double *num);
+
 /* Extract boolean this (must be a true or false) */
 bool json_tok_bool(const char *buffer, const jsmntok_t *tok, bool *b);
 
diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c
index 54547b955..cd0b3caf3 100644
--- a/lightningd/jsonrpc.c
+++ b/lightningd/jsonrpc.c
@@ -24,6 +24,7 @@
 #include <lightningd/lightningd.h>
 #include <lightningd/log.h>
 #include <lightningd/options.h>
+#include <lightningd/param.h>
 #include <stdio.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
@@ -95,14 +96,13 @@ static void json_rhash(struct command *cmd,
 		       const char *buffer, const jsmntok_t *params)
 {
 	struct json_result *response = new_json_result(cmd);
-	jsmntok_t *secrettok;
+	const jsmntok_t *secrettok;
 	struct sha256 secret;
 
-	if (!json_get_params(cmd, buffer, params,
-			     "secret", &secrettok,
-			     NULL)) {
+	if (!param(cmd, buffer, params,
+		   p_req("secret", json_tok_tok, &secrettok),
+		   NULL))
 		return;
-	}
 
 	if (!hex_decode(buffer + secrettok->start,
 			secrettok->end - secrettok->start,
@@ -200,11 +200,12 @@ static void json_help(struct command *cmd,
 	unsigned int i;
 	struct json_result *response = new_json_result(cmd);
 	struct json_command **cmdlist = get_cmdlist();
-	jsmntok_t *cmdtok;
+	const jsmntok_t *cmdtok;
 
-	if (!json_get_params(cmd, buffer, params, "?command", &cmdtok, NULL)) {
+	if (!param(cmd, buffer, params,
+		   p_opt_tok("command", &cmdtok),
+		   NULL))
 		return;
-	}
 
 	json_object_start(response, NULL);
 	if (cmdtok) {
@@ -493,104 +494,6 @@ static void parse_request(struct json_connection *jcon, const jsmntok_t tok[])
 		assert(c->pending);
 }
 
-bool json_get_params(struct command *cmd,
-		     const char *buffer, const jsmntok_t param[], ...)
-{
-	va_list ap;
-	const char **names;
-	size_t num_names;
-	 /* Uninitialized warnings on p and end */
-	const jsmntok_t *p = NULL, *end = NULL;
-
-	if (param->type == JSMN_ARRAY) {
-		if (param->size == 0)
-			p = NULL;
-		else
-			p = param + 1;
-		end = json_next(param);
-	} else if (param->type != JSMN_OBJECT) {
-		command_fail(cmd, JSONRPC2_INVALID_PARAMS,
-			     "Expected array or object for params");
-		return false;
-	}
-
-	num_names = 0;
-	names = tal_arr(cmd, const char *, num_names + 1);
-	va_start(ap, param);
-	while ((names[num_names] = va_arg(ap, const char *)) != NULL) {
-		const jsmntok_t **tokptr = va_arg(ap, const jsmntok_t **);
-		bool compulsory = true;
-		if (names[num_names][0] == '?') {
-			names[num_names]++;
-			compulsory = false;
-		}
-		if (param->type == JSMN_ARRAY) {
-			*tokptr = p;
-			if (p) {
-				p = json_next(p);
-				if (p == end)
-					p = NULL;
-			}
-		} else {
-			*tokptr = json_get_member(buffer, param,
-						  names[num_names]);
-		}
-		/* Convert 'null' to NULL */
-		if (*tokptr
-		    && (*tokptr)->type == JSMN_PRIMITIVE
-		    && buffer[(*tokptr)->start] == 'n') {
-			*tokptr = NULL;
-		}
-		if (compulsory && !*tokptr) {
-			va_end(ap);
-			command_fail(cmd, JSONRPC2_INVALID_PARAMS,
-				     "Missing '%s' parameter",
-				      names[num_names]);
-			return false;
-		}
-		num_names++;
-		tal_resize(&names, num_names + 1);
-	}
-
-	va_end(ap);
-
-	/* Now make sure there aren't any params which aren't valid */
-	if (param->type == JSMN_ARRAY) {
-		if (param->size > num_names) {
-			tal_free(names);
-			command_fail(cmd, JSONRPC2_INVALID_PARAMS,
-				     "Too many parameters:"
-				     " got %u, expected %zu",
-				     param->size, num_names);
-			return false;
-		}
-	} else {
-		const jsmntok_t *t;
-
-		end = json_next(param);
-
-		/* Find each parameter among the valid names */
-		for (t = param + 1; t < end; t = json_next(t+1)) {
-			bool found = false;
-			for (size_t i = 0; i < num_names; i++) {
-				if (json_tok_streq(buffer, t, names[i]))
-					found = true;
-			}
-			if (!found) {
-				tal_free(names);
-				command_fail(cmd, JSONRPC2_INVALID_PARAMS,
-					     "Unknown parameter '%.*s'",
-					     t->end - t->start,
-					     buffer + t->start);
-				return false;
-			}
-		}
-	}
-
-	tal_free(names);
-	return true;
-}
-
 static struct io_plan *write_json(struct io_conn *conn,
 				  struct json_connection *jcon)
 {
@@ -864,6 +767,17 @@ json_tok_address_scriptpubkey(const tal_t *cxt,
 	return ADDRESS_PARSE_UNRECOGNIZED;
 }
 
+bool json_tok_newaddr(const char *buffer, const jsmntok_t *tok, bool *is_p2wpkh)
+{
+	if (json_tok_streq(buffer, tok, "p2sh-segwit"))
+		*is_p2wpkh = false;
+	else if (json_tok_streq(buffer, tok, "bech32"))
+		*is_p2wpkh = true;
+	else
+		return false;
+	return true;
+}
+
 bool json_tok_wtx(struct wallet_tx * tx, const char * buffer,
                   const jsmntok_t *sattok)
 {
diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h
index bb7715ef5..01ece3c3b 100644
--- a/lightningd/jsonrpc.h
+++ b/lightningd/jsonrpc.h
@@ -60,16 +60,6 @@ struct json_command {
 	const char *verbose;
 };
 
-/* Get the parameters (by position or name).  Followed by triples of
- * of const char *name, const jsmntok_t **ret_ptr, then NULL.
- *
- * If name starts with '?' it is optional (and will be set to NULL
- * if it's a literal 'null' or not present).
- * Otherwise false is returned, and command_fail already called.
- */
-bool json_get_params(struct command *cmd,
-		     const char *buffer, const jsmntok_t param[], ...);
-
 struct json_result *null_response(const tal_t *ctx);
 void command_success(struct command *cmd, struct json_result *response);
 void PRINTF_FMT(3, 4) command_fail(struct command *cmd, int code,
@@ -103,6 +93,8 @@ json_tok_address_scriptpubkey(const tal_t *ctx,
 			      const char *buffer,
 			      const jsmntok_t *tok, const u8 **scriptpubkey);
 
+bool json_tok_newaddr(const char *buffer, const jsmntok_t *tok, bool *is_p2wpkh);
+
 /* Parse the satoshi token in wallet_tx. */
 bool json_tok_wtx(struct wallet_tx * tx, const char * buffer,
 		  const jsmntok_t * sattok);
diff --git a/lightningd/log.c b/lightningd/log.c
index 66c1adc4a..8b7dbc52e 100644
--- a/lightningd/log.c
+++ b/lightningd/log.c
@@ -18,6 +18,7 @@
 #include <lightningd/jsonrpc_errors.h>
 #include <lightningd/lightningd.h>
 #include <lightningd/options.h>
+#include <lightningd/param.h>
 #include <signal.h>
 #include <stdio.h>
 #include <sys/stat.h>
@@ -656,18 +657,11 @@ static void json_getlog(struct command *cmd,
 	struct json_result *response = new_json_result(cmd);
 	enum log_level minlevel;
 	struct log_book *lr = cmd->ld->log_book;
-	jsmntok_t *level;
 
-	if (!json_get_params(cmd, buffer, params, "?level", &level, NULL)) {
+	if (!param(cmd, buffer, params,
+		   p_opt_def("level", json_tok_loglevel, &minlevel, LOG_INFORM),
+		   NULL))
 		return;
-	}
-
-	if (!level)
-		minlevel = LOG_INFORM;
-	else if (!json_tok_loglevel(buffer, level, &minlevel)) {
-		command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid level param");
-		return;
-	}
 
 	json_object_start(response, NULL);
 	if (deprecated_apis)
diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c
index 587df7779..8f7c8a2c3 100644
--- a/lightningd/opening_control.c
+++ b/lightningd/opening_control.c
@@ -19,6 +19,7 @@
 #include <lightningd/lightningd.h>
 #include <lightningd/log.h>
 #include <lightningd/opening_control.h>
+#include <lightningd/param.h>
 #include <lightningd/peer_control.h>
 #include <lightningd/subd.h>
 #include <openingd/gen_opening_wire.h>
@@ -892,7 +893,7 @@ bool handle_opening_channel(struct lightningd *ld,
 static void json_fund_channel(struct command *cmd,
 			      const char *buffer, const jsmntok_t *params)
 {
-	jsmntok_t *desttok, *sattok;
+	const jsmntok_t *desttok, *sattok;
 	struct funding_channel * fc = tal(cmd, struct funding_channel);
 	u32 feerate_per_kw = get_feerate(cmd->ld->topology, FEERATE_NORMAL);
 	u8 *msg;
@@ -900,10 +901,12 @@ static void json_fund_channel(struct command *cmd,
 	fc->cmd = cmd;
 	fc->uc = NULL;
 	wtx_init(cmd, &fc->wtx);
-	if (!json_get_params(fc->cmd, buffer, params,
-			     "id", &desttok,
-			     "satoshi", &sattok, NULL))
+	if (!param(fc->cmd, buffer, params,
+		   p_req("id", json_tok_tok, &desttok),
+		   p_req("satoshi", json_tok_tok, &sattok),
+		   NULL))
 		return;
+
 	if (!json_tok_wtx(&fc->wtx, buffer, sattok))
 		return;
 	if (!pubkey_from_hexstr(buffer + desttok->start,
diff --git a/lightningd/options.c b/lightningd/options.c
index 6852cf536..13f8fc8c5 100644
--- a/lightningd/options.c
+++ b/lightningd/options.c
@@ -24,6 +24,7 @@
 #include <lightningd/lightningd.h>
 #include <lightningd/log.h>
 #include <lightningd/options.h>
+#include <lightningd/param.h>
 #include <lightningd/subd.h>
 #include <stdio.h>
 #include <string.h>
@@ -1059,12 +1060,13 @@ static void json_listconfigs(struct command *cmd,
 {
 	size_t i;
 	struct json_result *response = new_json_result(cmd);
-	jsmntok_t *configtok;
+	const jsmntok_t *configtok;
 	bool found = false;
 
-	if (!json_get_params(cmd, buffer, params, "?config", &configtok, NULL)) {
+	if (!param(cmd, buffer, params,
+		   p_opt_tok("config", &configtok),
+		   NULL))
 		return;
-	}
 
 	json_object_start(response, NULL);
 	if (!configtok)
diff --git a/lightningd/param.c b/lightningd/param.c
index e11e2d21d..7f685a5a1 100644
--- a/lightningd/param.c
+++ b/lightningd/param.c
@@ -49,7 +49,10 @@ struct fail_format {
 static struct fail_format fail_formats[] = {
 	{json_tok_bool, "'%s' should be 'true' or 'false', not '%.*s'"},
 	{json_tok_double, "'%s' should be a double, not '%.*s'"},
+	{json_tok_percent,
+	 "'%s' should be a double in range [0.0, 100.0], not '%.*s'"},
 	{json_tok_u64, "'%s' should be an unsigned 64 bit integer, not '%.*s'"},
+	{json_tok_newaddr, "'%s' should be 'bech32' or 'p2sh-segwit', not '%.*s'"},
 	{json_tok_number, "'%s' should be an integer, not '%.*s'"},
 	{json_tok_wtx,
 	 "'%s' should be 'all' or a positive integer greater than "
diff --git a/lightningd/pay.c b/lightningd/pay.c
index 9c1309efd..4caccfaff 100644
--- a/lightningd/pay.c
+++ b/lightningd/pay.c
@@ -11,6 +11,7 @@
 #include <lightningd/lightningd.h>
 #include <lightningd/log.h>
 #include <lightningd/options.h>
+#include <lightningd/param.h>
 #include <lightningd/peer_control.h>
 #include <lightningd/peer_htlcs.h>
 #include <lightningd/subd.h>
@@ -912,21 +913,19 @@ static void json_sendpay_on_resolve(const struct sendpay_result* r,
 static void json_sendpay(struct command *cmd,
 			 const char *buffer, const jsmntok_t *params)
 {
-	jsmntok_t *routetok, *rhashtok;
-	jsmntok_t *msatoshitok;
+	const jsmntok_t *routetok, *rhashtok;
 	const jsmntok_t *t, *end;
 	size_t n_hops;
 	struct sha256 rhash;
 	struct route_hop *route;
-	u64 msatoshi;
+	u64 *msatoshi;
 
-	if (!json_get_params(cmd, buffer, params,
-			     "route", &routetok,
-			     "payment_hash", &rhashtok,
-			     "?msatoshi", &msatoshitok,
-			     NULL)) {
+	if (!param(cmd, buffer, params,
+		   p_req("route", json_tok_tok, &routetok),
+		   p_req("payment_hash", json_tok_tok, &rhashtok),
+		   p_opt("msatoshi", json_tok_u64, &msatoshi),
+		   NULL))
 		return;
-	}
 
 	if (!hex_decode(buffer + rhashtok->start,
 			rhashtok->end - rhashtok->start,
@@ -1006,32 +1005,24 @@ static void json_sendpay(struct command *cmd,
 		return;
 	}
 
-	if (msatoshitok) {
-		if (!json_tok_u64(buffer, msatoshitok, &msatoshi)) {
-			command_fail(cmd, JSONRPC2_INVALID_PARAMS,
-				     "'%.*s' is not a number",
-				     msatoshitok->end - msatoshitok->start,
-				     buffer + msatoshitok->start);
-			return;
-		}
-		/* The given msatoshi is the actual payment that
-		 * the payee is requesting. The final hop amount, is
-		 * what we actually give, which can be from the
-		 * msatoshi to twice msatoshi. */
-		/* if not: msatoshi <= finalhop.amount <= 2 * msatoshi,
-		 * fail. */
-		if (!(msatoshi <= route[n_hops-1].amount &&
-		      route[n_hops-1].amount <= 2 * msatoshi)) {
+	/* The given msatoshi is the actual payment that the payee is
+	 * requesting. The final hop amount is what we actually give, which can
+	 * be from the msatoshi to twice msatoshi. */
+
+	/* if not: msatoshi <= finalhop.amount <= 2 * msatoshi, fail. */
+	if (msatoshi) {
+		if (!(*msatoshi <= route[n_hops-1].amount &&
+		      route[n_hops-1].amount <= 2 * *msatoshi)) {
 			command_fail(cmd, JSONRPC2_INVALID_PARAMS,
 				     "msatoshi %"PRIu64" out of range",
-				     msatoshi);
+				     *msatoshi);
 			return;
 		}
-	} else
-		msatoshi = route[n_hops-1].amount;
+	}
 
-	if (send_payment(cmd, cmd->ld, &rhash, route, msatoshi,
-			  &json_sendpay_on_resolve, cmd))
+	if (send_payment(cmd, cmd->ld, &rhash, route,
+			 msatoshi ? *msatoshi : route[n_hops-1].amount,
+			 &json_sendpay_on_resolve, cmd))
 		command_still_pending(cmd);
 }
 
@@ -1050,15 +1041,14 @@ static void waitsendpay_timeout(struct command *cmd)
 static void json_waitsendpay(struct command *cmd, const char *buffer,
 			     const jsmntok_t *params)
 {
-	jsmntok_t *rhashtok;
-	jsmntok_t *timeouttok;
+	const jsmntok_t *rhashtok;
 	struct sha256 rhash;
-	unsigned int timeout;
+	unsigned int *timeout;
 
-	if (!json_get_params(cmd, buffer, params,
-			     "payment_hash", &rhashtok,
-			     "?timeout", &timeouttok,
-			     NULL))
+	if (!param(cmd, buffer, params,
+		   p_req("payment_hash", json_tok_tok, &rhashtok),
+		   p_opt("timeout", json_tok_number, &timeout),
+		   NULL))
 		return;
 
 	if (!hex_decode(buffer + rhashtok->start,
@@ -1071,21 +1061,12 @@ static void json_waitsendpay(struct command *cmd, const char *buffer,
 		return;
 	}
 
-	if (timeouttok && !json_tok_number(buffer, timeouttok, &timeout)) {
-		command_fail(cmd, JSONRPC2_INVALID_PARAMS,
-			     "'%.*s' is not a valid number",
-			     timeouttok->end - timeouttok->start,
-			     buffer + timeouttok->start);
-		return;
-	}
-
 	if (!wait_payment(cmd, cmd->ld, &rhash, &json_waitsendpay_on_resolve, cmd))
 		return;
 
-	if (timeouttok)
-		new_reltimer(&cmd->ld->timers, cmd, time_from_sec(timeout),
+	if (timeout)
+		new_reltimer(&cmd->ld->timers, cmd, time_from_sec(*timeout),
 			     &waitsendpay_timeout, cmd);
-
 	command_still_pending(cmd);
 }
 
@@ -1105,12 +1086,11 @@ static void json_listpayments(struct command *cmd, const char *buffer,
 	jsmntok_t *bolt11tok, *rhashtok;
 	struct sha256 *rhash = NULL;
 
-	if (!json_get_params(cmd, buffer, params,
-			     "?bolt11", &bolt11tok,
-			     "?payment_hash", &rhashtok,
-			     NULL)) {
+	if (!param(cmd, buffer, params,
+		   p_opt_tok("bolt11", &bolt11tok),
+		   p_opt_tok("payment_hash", &rhashtok),
+		   NULL))
 		return;
-	}
 
 	if (bolt11tok) {
 		struct bolt11 *b11;
diff --git a/lightningd/payalgo.c b/lightningd/payalgo.c
index 076fa7868..8b7ca115f 100644
--- a/lightningd/payalgo.c
+++ b/lightningd/payalgo.c
@@ -15,6 +15,7 @@
 #include <lightningd/jsonrpc_errors.h>
 #include <lightningd/lightningd.h>
 #include <lightningd/log.h>
+#include <lightningd/param.h>
 #include <lightningd/subd.h>
 #include <sodium/randombytes.h>
 #include <wallet/wallet.h>
@@ -592,29 +593,27 @@ static void json_pay_stop_retrying(struct pay *pay)
 static void json_pay(struct command *cmd,
 		     const char *buffer, const jsmntok_t *params)
 {
-	jsmntok_t *bolt11tok, *msatoshitok, *desctok, *riskfactortok, *maxfeetok;
-	jsmntok_t *retryfortok;
-	jsmntok_t *maxdelaytok;
-	double riskfactor = 1.0;
-	double maxfeepercent = 0.5;
-	u64 msatoshi;
+	const jsmntok_t *bolt11tok, *desctok;
+	double riskfactor;
+	double maxfeepercent;
+	u64 *msatoshi;
 	struct pay *pay = tal(cmd, struct pay);
 	struct bolt11 *b11;
 	char *fail, *b11str, *desc;
-	unsigned int retryfor = 60;
-	unsigned int maxdelay = cmd->ld->config.locktime_max;
-
-	if (!json_get_params(cmd, buffer, params,
-			     "bolt11", &bolt11tok,
-			     "?msatoshi", &msatoshitok,
-			     "?description", &desctok,
-			     "?riskfactor", &riskfactortok,
-			     "?maxfeepercent", &maxfeetok,
-			     "?retry_for", &retryfortok,
-			     "?maxdelay", &maxdelaytok,
-			     NULL)) {
+	unsigned int retryfor;
+	unsigned int maxdelay;
+
+	if (!param(cmd, buffer, params,
+		   p_req("bolt11", json_tok_tok, &bolt11tok),
+		   p_opt("msatoshi", json_tok_u64, &msatoshi),
+		   p_opt_tok("description", &desctok),
+		   p_opt_def("riskfactor", json_tok_double, &riskfactor, 1.0),
+		   p_opt_def("maxfeepercent", json_tok_percent, &maxfeepercent, 0.5),
+		   p_opt_def("retry_for", json_tok_number, &retryfor, 60),
+		   p_opt_def("maxdelay", json_tok_number, &maxdelay,
+			     cmd->ld->config.locktime_max),
+		   NULL))
 		return;
-	}
 
 	b11str = tal_strndup(cmd, buffer + bolt11tok->start,
 			     bolt11tok->end - bolt11tok->start);
@@ -638,78 +637,24 @@ static void json_pay(struct command *cmd,
 	pay->expiry.ts.tv_sec = b11->timestamp + b11->expiry;
 	pay->min_final_cltv_expiry = b11->min_final_cltv_expiry;
 
-	if (retryfortok && !json_tok_number(buffer, retryfortok, &retryfor)) {
-		command_fail(cmd, JSONRPC2_INVALID_PARAMS,
-			     "'%.*s' is not an integer",
-			     retryfortok->end - retryfortok->start,
-			     buffer + retryfortok->start);
-		return;
-	}
-
 	if (b11->msatoshi) {
-		msatoshi = *b11->msatoshi;
-		if (msatoshitok) {
+		if (msatoshi) {
 			command_fail(cmd, JSONRPC2_INVALID_PARAMS,
 				     "msatoshi parameter unnecessary");
 			return;
 		}
+		msatoshi = b11->msatoshi;
 	} else {
-		if (!msatoshitok) {
+		if (!msatoshi) {
 			command_fail(cmd, JSONRPC2_INVALID_PARAMS,
 				     "msatoshi parameter required");
 			return;
 		}
-		if (!json_tok_u64(buffer, msatoshitok, &msatoshi)) {
-			command_fail(cmd, JSONRPC2_INVALID_PARAMS,
-				     "msatoshi '%.*s' is not a valid number",
-				     msatoshitok->end-msatoshitok->start,
-				     buffer + msatoshitok->start);
-			return;
-		}
-	}
-	pay->msatoshi = msatoshi;
-
-	if (riskfactortok
-	    && !json_tok_double(buffer, riskfactortok, &riskfactor)) {
-		command_fail(cmd, JSONRPC2_INVALID_PARAMS,
-			     "'%.*s' is not a valid double",
-			     riskfactortok->end - riskfactortok->start,
-			     buffer + riskfactortok->start);
-		return;
 	}
+	pay->msatoshi = *msatoshi;
 	pay->riskfactor = riskfactor * 1000;
-
-	if (maxfeetok
-	    && !json_tok_double(buffer, maxfeetok, &maxfeepercent)) {
-		command_fail(cmd, JSONRPC2_INVALID_PARAMS,
-			     "'%.*s' is not a valid double",
-			     maxfeetok->end - maxfeetok->start,
-			     buffer + maxfeetok->start);
-		return;
-	}
-	/* Ensure it is in range 0.0 <= maxfeepercent <= 100.0 */
-	if (!(0.0 <= maxfeepercent)) {
-		command_fail(cmd, JSONRPC2_INVALID_PARAMS,
-			     "%f maxfeepercent must be non-negative",
-			     maxfeepercent);
-		return;
-	}
-	if (!(maxfeepercent <= 100.0)) {
-		command_fail(cmd, JSONRPC2_INVALID_PARAMS,
-			     "%f maxfeepercent must be <= 100.0",
-			     maxfeepercent);
-		return;
-	}
 	pay->maxfeepercent = maxfeepercent;
 
-	if (maxdelaytok
-	    && !json_tok_number(buffer, maxdelaytok, &maxdelay)) {
-		command_fail(cmd, JSONRPC2_INVALID_PARAMS,
-			     "'%.*s' is not a valid integer",
-			     maxdelaytok->end - maxdelaytok->start,
-			     buffer + maxdelaytok->start);
-		return;
-	}
 	if (maxdelay < pay->min_final_cltv_expiry) {
 		command_fail(cmd, JSONRPC2_INVALID_PARAMS,
 			     "maxdelay (%u) must be greater than "
diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c
index eb9f49ec9..bf4e17d27 100644
--- a/lightningd/peer_control.c
+++ b/lightningd/peer_control.c
@@ -39,6 +39,7 @@
 #include <lightningd/onchain_control.h>
 #include <lightningd/opening_control.h>
 #include <lightningd/options.h>
+#include <lightningd/param.h>
 #include <lightningd/peer_htlcs.h>
 #include <unistd.h>
 #include <wally_bip32.h>
@@ -143,7 +144,7 @@ struct peer *peer_by_id(struct lightningd *ld, const struct pubkey *id)
 
 struct peer *peer_from_json(struct lightningd *ld,
 			    const char *buffer,
-			    jsmntok_t *peeridtok)
+			    const jsmntok_t *peeridtok)
 {
 	struct pubkey peerid;
 
@@ -940,38 +941,14 @@ static void gossipd_getpeers_complete(struct subd *gossip, const u8 *msg,
 static void json_listpeers(struct command *cmd,
 			  const char *buffer, const jsmntok_t *params)
 {
-	jsmntok_t *leveltok;
 	struct getpeers_args *gpa = tal(cmd, struct getpeers_args);
-	jsmntok_t *idtok;
 
 	gpa->cmd = cmd;
-	gpa->specific_id = NULL;
-	if (!json_get_params(cmd, buffer, params,
-			     "?id", &idtok,
-			     "?level", &leveltok,
-			     NULL)) {
+	if (!param(cmd, buffer, params,
+		   p_opt("id", json_tok_pubkey, &gpa->specific_id),
+		   p_opt("level", json_tok_loglevel, &gpa->ll),
+		   NULL))
 		return;
-	}
-
-	if (idtok) {
-		gpa->specific_id = tal_arr(cmd, struct pubkey, 1);
-		if (!json_tok_pubkey(buffer, idtok, gpa->specific_id)) {
-			command_fail(cmd, LIGHTNINGD,
-				     "id %.*s not valid",
-				     idtok->end - idtok->start,
-				     buffer + idtok->start);
-			return;
-		}
-	}
-	if (leveltok) {
-		gpa->ll = tal(gpa, enum log_level);
-		if (!json_tok_loglevel(buffer, leveltok, gpa->ll)) {
-			command_fail(cmd, LIGHTNINGD,
-				     "Invalid level param");
-			return;
-		}
-	} else
-		gpa->ll = NULL;
 
 	/* Get peers from gossipd. */
 	subd_req(cmd, cmd->ld->gossip,
@@ -1040,36 +1017,18 @@ command_find_channel(struct command *cmd,
 static void json_close(struct command *cmd,
 		       const char *buffer, const jsmntok_t *params)
 {
-	jsmntok_t *idtok;
-	jsmntok_t *timeouttok;
-	jsmntok_t *forcetok;
+	const jsmntok_t *idtok;
 	struct peer *peer;
 	struct channel *channel;
-	unsigned int timeout = 30;
-	bool force = false;
-
-	if (!json_get_params(cmd, buffer, params,
-			     "id", &idtok,
-			     "?force", &forcetok,
-			     "?timeout", &timeouttok,
-			     NULL)) {
-		return;
-	}
+	unsigned int timeout;
+	bool force;
 
-	if (forcetok && !json_tok_bool(buffer, forcetok, &force)) {
-		command_fail(cmd, JSONRPC2_INVALID_PARAMS,
-			     "Force '%.*s' must be true or false",
-			     forcetok->end - forcetok->start,
-			     buffer + forcetok->start);
+	if (!param(cmd, buffer, params,
+		   p_req("id", json_tok_tok, &idtok),
+		   p_opt_def("force", json_tok_bool, &force, false),
+		   p_opt_def("timeout", json_tok_number, &timeout, 30),
+		   NULL))
 		return;
-	}
-	if (timeouttok && !json_tok_number(buffer, timeouttok, &timeout)) {
-		command_fail(cmd, JSONRPC2_INVALID_PARAMS,
-			     "Timeout '%.*s' is not a number",
-			     timeouttok->end - timeouttok->start,
-			     buffer + timeouttok->start);
-		return;
-	}
 
 	peer = peer_from_json(cmd->ld, buffer, idtok);
 	if (peer)
@@ -1194,22 +1153,13 @@ static void gossip_peer_disconnected (struct subd *gossip,
 static void json_disconnect(struct command *cmd,
 			 const char *buffer, const jsmntok_t *params)
 {
-	jsmntok_t *idtok;
 	struct pubkey id;
 	u8 *msg;
 
-	if (!json_get_params(cmd, buffer, params,
-			     "id", &idtok,
-			     NULL)) {
+	if (!param(cmd, buffer, params,
+		   p_req("id", json_tok_pubkey, &id),
+		   NULL))
 		return;
-	}
-
-	if (!json_tok_pubkey(buffer, idtok, &id)) {
-		command_fail(cmd, LIGHTNINGD, "id %.*s not valid",
-			     idtok->end - idtok->start,
-			     buffer + idtok->start);
-		return;
-	}
 
 	msg = towire_gossipctl_peer_disconnect(cmd, &id);
 	subd_req(cmd, cmd->ld->gossip, msg, -1, 0, gossip_peer_disconnected, cmd);
@@ -1227,19 +1177,18 @@ AUTODATA(json_command, &disconnect_command);
 static void json_sign_last_tx(struct command *cmd,
 			      const char *buffer, const jsmntok_t *params)
 {
-	jsmntok_t *peertok;
+	struct pubkey peerid;
 	struct peer *peer;
 	struct json_result *response = new_json_result(cmd);
 	u8 *linear;
 	struct channel *channel;
 
-	if (!json_get_params(cmd, buffer, params,
-			     "id", &peertok,
-			     NULL)) {
+	if (!param(cmd, buffer, params,
+		   p_req("id", json_tok_pubkey, &peerid),
+		   NULL))
 		return;
-	}
 
-	peer = peer_from_json(cmd->ld, buffer, peertok);
+	peer = peer_by_id(cmd->ld, &peerid);
 	if (!peer) {
 		command_fail(cmd, LIGHTNINGD,
 			     "Could not find peer with that id");
@@ -1274,17 +1223,16 @@ AUTODATA(json_command, &dev_sign_last_tx);
 static void json_dev_fail(struct command *cmd,
 			  const char *buffer, const jsmntok_t *params)
 {
-	jsmntok_t *peertok;
+	struct pubkey peerid;
 	struct peer *peer;
 	struct channel *channel;
 
-	if (!json_get_params(cmd, buffer, params,
-			     "id", &peertok,
-			     NULL)) {
+	if (!param(cmd, buffer, params,
+		   p_req("id", json_tok_pubkey, &peerid),
+		   NULL))
 		return;
-	}
 
-	peer = peer_from_json(cmd->ld, buffer, peertok);
+	peer = peer_by_id(cmd->ld, &peerid);
 	if (!peer) {
 		command_fail(cmd, LIGHTNINGD,
 			     "Could not find peer with that id");
@@ -1320,18 +1268,17 @@ static void dev_reenable_commit_finished(struct subd *channeld UNUSED,
 static void json_dev_reenable_commit(struct command *cmd,
 				     const char *buffer, const jsmntok_t *params)
 {
-	jsmntok_t *peertok;
+	struct pubkey peerid;
 	struct peer *peer;
 	u8 *msg;
 	struct channel *channel;
 
-	if (!json_get_params(cmd, buffer, params,
-			     "id", &peertok,
-			     NULL)) {
+	if (!param(cmd, buffer, params,
+		   p_req("id", json_tok_pubkey, &peerid),
+		   NULL))
 		return;
-	}
 
-	peer = peer_from_json(cmd->ld, buffer, peertok);
+	peer = peer_by_id(cmd->ld, &peerid);
 	if (!peer) {
 		command_fail(cmd, LIGHTNINGD,
 			     "Could not find peer with that id");
@@ -1408,33 +1355,30 @@ static void process_dev_forget_channel(struct bitcoind *bitcoind UNUSED,
 static void json_dev_forget_channel(struct command *cmd, const char *buffer,
 				    const jsmntok_t *params)
 {
-	jsmntok_t *nodeidtok, *forcetok, *scidtok;
+	struct pubkey peerid;
 	struct peer *peer;
 	struct channel *channel;
-	struct short_channel_id scid;
+	struct short_channel_id *scid;
 	struct dev_forget_channel_cmd *forget = tal(cmd, struct dev_forget_channel_cmd);
 	forget->cmd = cmd;
-	if (!json_get_params(cmd, buffer, params,
-			     "id", &nodeidtok,
-			     "?short_channel_id", &scidtok,
-			     "?force", &forcetok,
-			     NULL)) {
-		return;
-	}
 
-	if (scidtok && !json_tok_short_channel_id(buffer, scidtok, &scid)) {
-		command_fail(cmd, JSONRPC2_INVALID_PARAMS,
-			     "Invalid short_channel_id '%.*s'",
-			     scidtok->end - scidtok->start,
-			     buffer + scidtok->start);
+	/* If &forget->force is used directly in p_opt_def() below then
+	 * gcc 7.3.0 fails with:
+	 * 'operation on ‘forget->force’ may be undefined [-Werror=sequence-point]'
+	 *
+	 * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86584
+	 *
+	 * Hence this indirection.
+	 */
+	bool *force = &forget->force;
+	if (!param(cmd, buffer, params,
+		   p_req("id", json_tok_pubkey, &peerid),
+		   p_opt("short_channel_id", json_tok_short_channel_id, &scid),
+		   p_opt_def("force", json_tok_bool, force, false),
+		   NULL))
 		return;
-	}
-
-	forget->force = false;
-	if (forcetok)
-		json_tok_bool(buffer, forcetok, &forget->force);
 
-	peer = peer_from_json(cmd->ld, buffer, nodeidtok);
+	peer = peer_by_id(cmd->ld, &peerid);
 	if (!peer) {
 		command_fail(cmd, LIGHTNINGD,
 			     "Could not find channel with that peer");
@@ -1443,10 +1387,10 @@ static void json_dev_forget_channel(struct command *cmd, const char *buffer,
 
 	forget->channel = NULL;
 	list_for_each(&peer->channels, channel, list) {
-		if (scidtok) {
+		if (scid) {
 			if (!channel->scid)
 				continue;
-			if (!short_channel_id_eq(channel->scid, &scid))
+			if (!short_channel_id_eq(channel->scid, scid))
 				continue;
 		}
 		if (forget->channel) {
diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h
index 642fbc02b..a84009b09 100644
--- a/lightningd/peer_control.h
+++ b/lightningd/peer_control.h
@@ -62,7 +62,7 @@ void delete_peer(struct peer *peer);
 struct peer *peer_by_id(struct lightningd *ld, const struct pubkey *id);
 struct peer *peer_from_json(struct lightningd *ld,
 			    const char *buffer,
-			    jsmntok_t *peeridtok);
+			    const jsmntok_t *peeridtok);
 
 /* The three ways peers enter from the network:
  *
diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c
index 23e9b5d1c..94d76033e 100644
--- a/lightningd/peer_htlcs.c
+++ b/lightningd/peer_htlcs.c
@@ -10,10 +10,12 @@
 #include <gossipd/gen_gossip_wire.h>
 #include <lightningd/chaintopology.h>
 #include <lightningd/htlc_end.h>
+#include <lightningd/json.h>
 #include <lightningd/jsonrpc.h>
 #include <lightningd/jsonrpc_errors.h>
 #include <lightningd/lightningd.h>
 #include <lightningd/log.h>
+#include <lightningd/param.h>
 #include <lightningd/pay.h>
 #include <lightningd/peer_control.h>
 #include <lightningd/peer_htlcs.h>
@@ -1651,30 +1653,24 @@ void notify_feerate_change(struct lightningd *ld)
 static void json_dev_ignore_htlcs(struct command *cmd, const char *buffer,
 				  const jsmntok_t *params)
 {
-	jsmntok_t *nodeidtok, *ignoretok;
+	struct pubkey peerid;
 	struct peer *peer;
+	bool ignore;
 
-	if (!json_get_params(cmd, buffer, params,
-			     "id", &nodeidtok,
-			     "ignore", &ignoretok,
-			     NULL)) {
+	if (!param(cmd, buffer, params,
+		   p_req("id", json_tok_pubkey, &peerid),
+		   p_req("ignore", json_tok_bool, &ignore),
+		   NULL))
 		return;
-	}
 
-	peer = peer_from_json(cmd->ld, buffer, nodeidtok);
+	peer = peer_by_id(cmd->ld, &peerid);
 	if (!peer) {
 		command_fail(cmd, LIGHTNINGD,
 			     "Could not find channel with that peer");
 		return;
 	}
+	peer->ignore_htlcs = ignore;
 
-	if (!json_tok_bool(buffer, ignoretok, &peer->ignore_htlcs)) {
-		command_fail(cmd, JSONRPC2_INVALID_PARAMS,
-			     "Invalid boolean '%.*s'",
-			     ignoretok->end - ignoretok->start,
-			     buffer + ignoretok->start);
-		return;
-	}
 	command_success(cmd, null_response(cmd));
 }
 
diff --git a/lightningd/test/run-param.c b/lightningd/test/run-param.c
index de69af0c5..746fdbccd 100644
--- a/lightningd/test/run-param.c
+++ b/lightningd/test/run-param.c
@@ -37,13 +37,13 @@ void command_fail_detailed(struct command *cmd, int code,
 }
 
 /* AUTOGENERATED MOCKS START */
+/* Generated stub for json_tok_newaddr */
+bool json_tok_newaddr(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool *is_p2wpkh UNNEEDED)
+{ fprintf(stderr, "json_tok_newaddr called!\n"); abort(); }
 /* Generated stub for json_tok_wtx */
-bool json_tok_wtx(struct wallet_tx *tx UNNEEDED, const char *buffer UNNEEDED,
+bool json_tok_wtx(struct wallet_tx * tx UNNEEDED, const char * buffer UNNEEDED,
 		  const jsmntok_t * sattok UNNEEDED)
-{
-	abort();
-}
-
+{ fprintf(stderr, "json_tok_wtx called!\n"); abort(); }
 /* AUTOGENERATED MOCKS END */
 
 struct json {
diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c
index f9b25af53..338ea031c 100644
--- a/wallet/test/run-wallet.c
+++ b/wallet/test/run-wallet.c
@@ -242,10 +242,6 @@ struct json_escaped *json_escape(const tal_t *ctx UNNEEDED, const char *str TAKE
 struct json_escaped *json_escaped_string_(const tal_t *ctx UNNEEDED,
 					  const void *bytes UNNEEDED, size_t len UNNEEDED)
 { fprintf(stderr, "json_escaped_string_ called!\n"); abort(); }
-/* Generated stub for json_get_params */
-bool json_get_params(struct command *cmd UNNEEDED,
-		     const char *buffer UNNEEDED, const jsmntok_t param[] UNNEEDED, ...)
-{ fprintf(stderr, "json_get_params called!\n"); abort(); }
 /* Generated stub for json_object_end */
 void json_object_end(struct json_result *ptr UNNEEDED)
 { fprintf(stderr, "json_object_end called!\n"); abort(); }
@@ -275,6 +271,10 @@ bool json_tok_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
 bool json_tok_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
 			       struct short_channel_id *scid UNNEEDED)
 { fprintf(stderr, "json_tok_short_channel_id called!\n"); abort(); }
+/* Generated stub for json_tok_tok */
+bool json_tok_tok(const char *buffer UNNEEDED, const jsmntok_t * tok UNNEEDED,
+		  const jsmntok_t **out UNNEEDED)
+{ fprintf(stderr, "json_tok_tok called!\n"); abort(); }
 /* Generated stub for kill_uncommitted_channel */
 void kill_uncommitted_channel(struct uncommitted_channel *uc UNNEEDED,
 			      const char *why UNNEEDED)
@@ -312,6 +312,10 @@ struct outpointfilter *outpointfilter_new(tal_t *ctx UNNEEDED)
 void outpointfilter_remove(struct outpointfilter *of UNNEEDED,
 			   const struct bitcoin_txid *txid UNNEEDED, const u32 outnum UNNEEDED)
 { fprintf(stderr, "outpointfilter_remove called!\n"); abort(); }
+/* Generated stub for param */
+bool param(struct command *cmd UNNEEDED, const char *buffer UNNEEDED,
+	   const jsmntok_t params[] UNNEEDED, ...)
+{ fprintf(stderr, "param called!\n"); abort(); }
 /* Generated stub for peer_accept_channel */
 u8 *peer_accept_channel(const tal_t *ctx UNNEEDED,
 			struct lightningd *ld UNNEEDED,
diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c
index 28ce72f3c..72dec9a46 100644
--- a/wallet/walletrpc.c
+++ b/wallet/walletrpc.c
@@ -19,6 +19,7 @@
 #include <lightningd/jsonrpc_errors.h>
 #include <lightningd/lightningd.h>
 #include <lightningd/log.h>
+#include <lightningd/param.h>
 #include <lightningd/peer_control.h>
 #include <lightningd/subd.h>
 #include <wally_bip32.h>
@@ -86,7 +87,7 @@ static void wallet_withdrawal_broadcast(struct bitcoind *bitcoind UNUSED,
 static void json_withdraw(struct command *cmd,
 			  const char *buffer, const jsmntok_t *params)
 {
-	jsmntok_t *desttok, *sattok;
+	const jsmntok_t *desttok, *sattok;
 	struct withdrawal *withdraw = tal(cmd, struct withdrawal);
 
 	u32 feerate_per_kw = get_feerate(cmd->ld->topology, FEERATE_NORMAL);
@@ -96,10 +97,13 @@ static void json_withdraw(struct command *cmd,
 
 	withdraw->cmd = cmd;
 	wtx_init(cmd, &withdraw->wtx);
-	if (!json_get_params(withdraw->cmd, buffer, params,
-			     "destination", &desttok,
-			     "satoshi", &sattok, NULL))
+
+	if (!param(cmd, buffer, params,
+		   p_req("destination", json_tok_tok, &desttok),
+		   p_req("satoshi", json_tok_tok, &sattok),
+		   NULL))
 		return;
+
 	if (!json_tok_wtx(&withdraw->wtx, buffer, sattok))
 		return;
 
@@ -217,26 +221,14 @@ static void json_newaddr(struct command *cmd, const char *buffer UNUSED,
 	struct json_result *response = new_json_result(cmd);
 	struct ext_key ext;
 	struct pubkey pubkey;
-	jsmntok_t *addrtype;
 	bool is_p2wpkh;
 	s64 keyidx;
 	char *out;
 
-	if (!json_get_params(cmd, buffer, params,
-			     "?addresstype", &addrtype, NULL)) {
-		return;
-	}
-
-	if (!addrtype || json_tok_streq(buffer, addrtype, "p2sh-segwit"))
-		is_p2wpkh = false;
-	else if (json_tok_streq(buffer, addrtype, "bech32"))
-		is_p2wpkh = true;
-	else {
-		command_fail(cmd, JSONRPC2_INVALID_PARAMS,
-			     "Invalid address type "
-			     "(expected bech32 or p2sh-segwit)");
+	if (!param(cmd, buffer, params,
+		   p_opt_def("addresstype", json_tok_newaddr, &is_p2wpkh, true),
+		   NULL))
 		return;
-	}
 
 	keyidx = wallet_get_newindex(cmd->ld);
 	if (keyidx < 0) {
@@ -287,18 +279,15 @@ static void json_listaddrs(struct command *cmd,
 	struct json_result *response = new_json_result(cmd);
 	struct ext_key ext;
 	struct pubkey pubkey;
-	jsmntok_t *bip32tok;
 	u64 bip32_max_index;
 
-	if (!json_get_params(cmd, buffer, params,
-			     "?bip32_max_index", &bip32tok,
-			     NULL)) {
+	if (!param(cmd, buffer, params,
+		   p_opt_def("bip32_max_index", json_tok_u64, &bip32_max_index,
+			     db_get_intvar(cmd->ld->wallet->db,
+					   "bip32_max_index", 0)),
+		   NULL))
 		return;
-	}
 
-	if (!bip32tok || !json_tok_u64(buffer, bip32tok, &bip32_max_index)) {
-		bip32_max_index = db_get_intvar(cmd->ld->wallet->db, "bip32_max_index", 0);
-	}
 	json_object_start(response, NULL);
 	json_array_start(response, "addresses");